题意:给一个01矩阵,问是否能找到一个行的集合,使得集合中每列出现且仅出现一次‘1’。
思路:舞蹈链X算法模版题。来谈谈我对这个算法的理解。。这个算法是一个深搜,但是它的数据结构非常巧妙,是一个循环十字链表,把矩阵内为1的元素建立节点,都链起来,还加上了一个“表头”。每次先选一列没找到‘1’的列,如果没有,则寻找失败;如果有,选择一行,把这行,这行有‘1’的列,与这行有公共列出现‘1’的行统统在链表中暂时隐去。。然后往下深搜,若出现所有列都被隐去的情况,则寻找成功。因为递归过程中,被“隐藏”的节点的指针没有被改变,只是从h遍历不到它,所以递归(切断一些链)和回溯(复原一些链)十分高效。。
#include<iostream> #include<cmath> #include<queue> #include<map> #include<set> #include<vector> #include<algorithm> #include<string.h> #include<cstdio> using namespace std; #define maxn 310 #define maxnode maxn*18 struct DLX{ int n,sz; int S[maxn]; int row[maxnode],col[maxnode]; int L[maxnode],R[maxnode],U[maxnode],D[maxnode]; int ansd,ans[maxn]; void init(int n){ this->n=n; for(int i=0;i<=n;i++){ U[i]=i; D[i]=i; L[i]=i-1; R[i]=i+1; } R[n]=0; L[0]=n; sz=n+1; memset(S,0,sizeof(S)); } void addRow(int r,vector<int> columns){ if(columns.empty())return; int first=sz; int csiz=columns.size(); for(int i=0;i<csiz;i++){ int c=columns[i]; L[sz]=sz-1; R[sz]=sz+1; D[sz]=c; U[sz]=U[c]; D[U[c]]=sz; U[c]=sz; row[sz]=r; col[sz]=c; S[c]++; sz++; } R[sz-1]=first; L[first]=sz-1; } #define FOR(i,A,s) for(int i=A[s];i!=s;i=A[i]) void remove(int c){ L[R[c]]=L[c]; R[L[c]]=R[c]; FOR(i,D,c){ FOR(j,R,i){ U[D[j]]=U[j]; D[U[j]]=D[j]; --S[col[j]]; } } } void restore(int c){ FOR(i,U,c){ FOR(j,L,i){ ++S[col[j]]; U[D[j]]=j; D[U[j]]=j; } } L[R[c]]=c; R[L[c]]=c; } bool dfs(int d){ if(R[0]==0){ ansd=d; return 1; } int c=R[0]; FOR(i,R,0){ if(S[i]<S[c])c=i; } remove(c); FOR(i,D,c){ ans[d]=row[i]; FOR(j,R,i){ remove(col[j]); } if(dfs(d+1))return 1; FOR(j,L,i){ restore(col[j]); } } restore(c); return 0; } bool solve(vector<int>& v){ v.clear(); if(!dfs(0))return 0; for(int i=0;i<ansd;i++)v.push_back(ans[i]); return 1; } }; DLX dlx; int main(){ int n,m; while(cin>>n>>m){ dlx.init(m); vector<int> vec; for(int i=1;i<=n;i++){ vec.clear(); for(int j=1;j<=m;j++){ int t; scanf("%d",&t); if(t)vec.push_back(j); } dlx.addRow(i,vec); } vector<int> a; if(dlx.solve(a)){ printf("Yes, I found it\n"); }else{ printf("It is impossible\n"); } } return 0; }