用2-SAT的思路將題目轉化爲:已知$n$個二元組$<x,y>$,能夠算出有多少屬於不一樣二元組的元素$(a,b)$存在衝突,要在每一個二元組$<x,y>$中選擇選擇一個元素,且要儘量的少選$y$,問是否能夠選取$n$個兩兩不相互矛盾的元素,若能夠輸出選取方案。c++
通過簡單的推導能夠獲得,對於$<x_i,y_i>$和$<x_j,y_j>$spa
繼續用2-SAT的思路,並結合上面的性質:code
這樣一來,咱們獲得了一個或多個聯通塊,根據以前推導的性質,對於第$i$個二元組,若是$i$的選取方法肯定了,那麼相同聯通塊裏的選取方法也就都肯定了,再加上不一樣聯通塊之間是互不影響的,咱們只須要對每一個聯通塊貪心的選取$y$少的方案,而後把全部聯通塊的答案加起來就是最終的答案了。get
跑Tarjan縮點或者並查集縮點維護一下就沒了。it
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=110; int n,m,k; ll a[55],b[55]; char s[55]; int fa[N],sz[N]; bool vis[N]; int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));} void merge(int x,int y){ x=find(x); y=find(y); if(x!=y){sz[y]+=sz[x];fa[x]=y;} } inline int getsame(ll x){ int cnt=0; while(x){cnt++;x-=x&(-x);} return m-cnt; } void solve(){ for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ bool c1=false,c2=false; if(getsame(a[i]^a[j])>=k)c1=true; if(getsame(a[i]^b[j])>=k)c2=true; if(c1 && c2)continue; if(!c1 && !c2){printf("-1\n");return;} if(c2)merge(i,j+n),merge(i+n,j); if(c1)merge(i,j),merge(i+n,j+n); } } vector<int>ans; for(int i=1;i<=n;i++){ int fi=find(i),fin=find(i+n); if(fi==fin){printf("-1\n");return;} if(vis[fi])continue; if(vis[fin]){ans.push_back(i);continue;} if(sz[fi]>sz[fin]){vis[fin]=true;ans.push_back(i);} else vis[fi]=true; } printf("%d\n",(int)ans.size()); for(int i:ans)printf("%d ",i); puts(""); } int main() { int T; scanf("%d",&T); while(T--){ scanf("%d %d %d",&n,&m,&k); for(int i=1;i<=2*n;i++)fa[i]=i, sz[i]=(i>n?1:0), vis[i]=false; for(int i=1;i<=n;i++){ scanf("%s",s); a[i]=0; b[i]=0; for(int j=0;s[j];j++)a[i]*=2, a[i]+=s[j]-'0'; reverse(s,s+m); for(int j=0;s[j];j++)b[i]*=2, b[i]+=s[j]-'0'; } solve(); } return 0; }