位运算裸搜版(500ms):
#include <cstdio> #include <cstring> #include <iostream> #define LL long long using namespace std; const int N=32; const int L=10; LL w[L],goal[L],x[20][L]; int m,n,a[20][320]; inline bool cont(LL *x,LL *y) { for (int i=0; i<m; i++) { if (x[i/N]&(1<<(i%N)) && y[i/N]&(1<<(i%N))) return true; } return false; } bool dfs(int i) { bool flag=true; for (int k=0; k<L && flag; k++) if (goal[k]!=w[k]) flag=false; if (flag) return true; if (i>n) return false; if (cont(w,x[i])) return dfs(i+1); LL tmp[L]; for (int k=0; k<L; k++) tmp[k]=w[k],w[k]+=x[i][k]; if (dfs(i+1)) return true; for (int k=0; k<L; k++) w[k]=tmp[k]; if (dfs(i+1)) return true; return false; } int main() { while (~scanf("%d%d",&n,&m)) { memset(x,0,sizeof(x)); for (int i=1; i<=n; i++) { for (int j=0; j<m; j++) { scanf("%d",&a[i][j]); if (a[i][j]) x[i][j/N]|=1<<(j%N); } } memset(goal,0,sizeof(goal)); for (int j=0; j<m; j++) goal[j/N]|=1<<(j%N); memset(w,0,sizeof(w)); if (dfs(1)) printf("Yes, I found it\n"); else printf("It is impossible\n"); } return 0; }
Dance Links版(63ms):
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int NN=6000; const int INF=10000000; int n,m,cnt,head,L[NN],R[NN],U[NN],D[NN],S[NN],C[NN],H[NN]; inline void add_link(int i,int j) { C[++cnt]=j; S[j]++; D[cnt]=j; U[cnt]=U[j]; if (H[i]) R[cnt]=H[i],L[cnt]=L[H[i]]; else R[cnt]=L[cnt]=cnt; H[i]=cnt; U[D[cnt]]=cnt; D[U[cnt]]=cnt; R[L[cnt]]=cnt; L[R[cnt]]=cnt; } void remove(int c)//删除第c列上的元素所在的行 { R[L[c]]=R[c]; L[R[c]]=L[c]; //删除c,c是列指针,删除了c就代表删除了整列,因为递归后不可能访问到c了 for (int i=D[c]; i!=c; i=D[i]) //c所在列上的元素i for (int j=R[i]; j!=i; j=R[j]) //i和j一行的,删掉j { U[D[j]]=U[j]; D[U[j]]=D[j]; S[C[j]]--;//j所在列的元素(‘1’的个数)-1; } } void resume(int c)//对应的恢复操作 { for (int i=U[c]; i!=c; i=U[i]) for (int j=L[i]; j!=i; j=L[j]) { D[U[j]]=j; U[D[j]]=j; S[C[j]]++; } L[R[c]]=R[L[c]]=c; } bool dance() { if (R[head]==head)//列指针删完了,任务完成 { puts("Yes, I found it"); return true; } int s(INF),c; for (int i=R[head]; i!=head; i=R[i]) if (S[i]<s) s=S[i],c=i; //找元素最少的列c,一种优化 remove(c);//删除列c for (int i=D[c]; i!=c; i=D[i]) { for (int j=R[i]; j!=i; j=R[j]) remove(C[j]);//删除所有可能的冲突元素 if (dance()) return true; for (int j=L[i]; j!=i; j=L[j]) resume(C[j]); } resume(c); return false; } int main() { int c; head=0; while (~scanf("%d%d",&n,&m)) { cnt=m; for (int i=0; i<=m; i++) { C[i]=U[i]=D[i]=i; //这里C[i]没有赋值时也过了,因为不会有对列指针取列标号的操作 L[i+1]=i; R[i]=i+1; S[i]=0; } L[0]=m; R[m]=0; //初始化十字链表 for (int i=1; i<=n; i++) { H[i]=0; for (int j=1; j<=m; j++) { c=getchar(); while (!isdigit(c)) c=getchar(); if (c=='1') add_link(i,j); } } if (!dance()) puts("It is impossible"); } return 0; }