poj 3740 Easy Finding

来源:互联网 发布:wordpress 百万数据 编辑:程序博客网 时间:2024/05/17 01:10

        题意:给一个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*18struct 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;}


0 0
原创粉丝点击