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