POJ 3740(跳舞链入门题)

题目链接:http://poj.org/problem?id=1155
数据结构:https://blog.csdn.net/weixin_39778570/article/details/86484040
ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443

题意
给你一个N*M的01矩阵,问是否能否找到一些行,使得每一列有且只有一个1

题解
算法讲解:https://mp.weixin.qq.com/s/2qgFaWUhdt7KlG2l2VlKQA
讲得挺好的,我就不重复了,233

Code

#include<stdio.h>
#include<cstring>
using namespace std;
const int N = 305, M = 25, ID=N*M; // 节点数 
struct DLX{
	int n,m,size;
	int U[ID],D[ID],L[ID],R[ID],Row[ID],Col[ID];
	int H[N], S[M];
	int ansd,ans[N];
	void init(int _n, int _m){
		n = _n;
		m = _m;
		for(int i=0; i<=m; i++){
			S[i]=0;
			U[i] = D[i] = i;
			L[i] = i-1;
			R[i] = i+1;
		} 
		L[0] = m;
		R[m] = 0;
		size = m;
		for(int i=1; i<=n; i++){
			H[i] = -1;
	 	}
	}
	void link(int r, int c){
		++S[Col[++size]=c];
		Row[size] = r;
		D[size] = D[c];
		U[D[c]] = size;
		U[size] = c;
		D[c] = size;
		if(H[r]<0){
			H[r] = L[size] = R[size] = size;
		} else{
			R[size] = R[H[r]];
			L[R[H[r]]] = size; 
			L[size] = H[r];
			R[H[r]] = size;
		}
	}
	void remove(int c){ // 删除列
		L[R[c]] = L[c]; R[L[c]] = R[c];
		for(int i=D[c]; i!=c; i=D[i]){ 
			for(int j=R[i]; j!=i; j=R[j]){ // 该列上其他的1的行删
				U[D[j]] = U[j];
				D[U[j]] = D[j];
				--S[Col[j]];
			}
		}
	}
	void resume(int c){ // 恢复列
		for(int i=U[c]; i!=c; i=U[i]){
			for(int j=L[i]; j!=i; j=L[j]){
				++S[Col[U[D[j]]=D[U[j]]=j]];
			}
		}
		L[R[c]] = R[L[c]] = c;
	}
	// d为递归深度
	bool Dance(int d){
		if(R[0]==0){
			ansd=d;
			return true;
		}
		int c = R[0];
		for(int i=R[0]; i!=0; i=R[i]){
			if(S[i]<S[c])
				c = i;
		}
		remove(c); // 指定列
		for(int i=D[c]; i!=c; i=D[i	]){
			ans[d]=Row[i]; // 选中指定列上1的行
			for(int j=R[i]; j!=i; j=R[j]){
				remove(Col[j]); // 改行上的其它1的列删除
			}
			if(Dance(d+1))return true;
			for(int j=L[i]; j!=i; j=L[j]){
				resume(Col[j]);
			}
		} 
		resume(c);
		return false;
	} 
};

DLX g;
int main(){
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF){
		g.init(n,m);
		for(int i=1; i<=n; i++){
			for(int j=1; j<=m; j++){
				int x;scanf("%d",&x);
				if(x==1)g.link(i,j); 
			}
		}	
		if(g.Dance(0))puts("Yes, I found it");
		else puts("It is impossible");
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值