poj 3740 Easy Finding

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值