【POJ3740】Easy Finding DLX(Dancing Links)精确覆盖问题

题意:多组数据。每组数据给你几行数,要求选出当中几行,使得每一列都有且仅有一个1,询问是可不可行,或者说能不能找出来。

题解:1、暴搜。2、DLX(Dancing links)。

本文写的是DLX。

算法參考白书P406或者http://www.cnblogs.com/grenet/p/3145800.html

我说一些仔细的东西,就是删除操作的形状是

|

——|————

——|————

——|————

被删除的点们之间的联系不用删,能够保留。准确地说它并非删去了这些点,而是删去这个形。

并且恢复时要反着恢复。

首先先确定从哪一列删除。进行一次remove。然后枚举这一列的每一行,对其进行remove,然后dfs,然后再resume。跳出循环时再resume确定列。

附代码,应该非常好看,仅仅要略耐心 一点点点点点。。我保证绝对照网上其它人的可读性强。

哦,对了。并不须要写点、行、列的单独的remove和resume函数,那太傻了,写模板也不须要,我想这应该永远用不上的。

。。。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 400
#define NN 10000
using namespace std;
struct DLX
{
	int U[NN],D[NN],L[NN],R[NN],C[NN];
	int H[NN],T[NN],cnt;
	inline void init(int m)
	{
		cnt=0;
		memset(H,0,sizeof(H));
		for(cnt=0;cnt<=m;cnt++)
		{
			U[cnt]=D[cnt]=C[cnt]=cnt;
			L[cnt]=cnt-1,R[cnt]=cnt+1;
		}
		L[0]=--cnt,R[cnt]=0;
	}
	inline void newnode(int x,int y)
	{
		C[++cnt]=y;T[y]++;

		if(!H[x])H[x]=L[cnt]=R[cnt]=cnt;
		else L[cnt]=H[x],R[cnt]=R[H[x]];
		R[H[x]]=L[R[H[x]]]=cnt,H[x]=cnt;

		U[cnt]=U[y],D[cnt]=y;
		U[y]=D[U[y]]=cnt;
	}
	inline void scan(int n,int m)
	{
		int i,j,k;
		for(i=1;i<=n;i++)for(j=1;j<=m;j++)
		{
			scanf("%d",&k);
			if(k)newnode(i,j);
		}
	}
	inline void remove(int x)
	{
		for(int i=D[x];i!=x;i=D[i])
		{
			for(int j=R[i];j!=i;j=R[j])
			{
				U[D[j]]=U[j];
				D[U[j]]=D[j];
				T[C[j]]--;
			}
		}
		L[R[x]]=L[x];
		R[L[x]]=R[x];
	}
	inline void resume(int x)
	{
		for(int i=U[x];i!=x;i=U[i])
		{
			for(int j=L[i];j!=i;j=L[j])
			{
				U[D[j]]=j;
				D[U[j]]=j;
				T[C[j]]++;
			}
		}
		L[R[x]]=x;
		R[L[x]]=x;
	}
	inline bool dfs()
	{
		if(!R[0])return true;
		int S=R[0],W=T[S],i,j;
		for(i=R[S];i;i=R[i])if(T[i]<W)
		{
			W=T[i];
			S=i;
		}
		remove(S);
		for(i=D[S];i!=S;i=D[i])
		{
			for(j=R[i];j!=i;j=R[j])remove(C[j]);
			if(dfs())return true;
			for(j=L[i];j!=i;j=L[j])resume(C[j]);
		}
		resume(S);
		return false;
	}
}dlx;
int main()
{
//	freopen("test.in","r",stdin);
//	freopen("my.out","w",stdout);
	int n,m;
	while(~scanf("%d%d",&n,&m))
	{
		dlx.init(m);
		dlx.scan(n,m);
		if(dlx.dfs())puts("Yes, I found it");
		else puts("It is impossible");
	}
	return 0;
}

给点福利。

专属那些输出调试者。

	inline void print_line(int x)
	{
		int i=x;
		do{
			printf("%d->",i);
			i=R[i];
		}while(i!=x);
		puts("");
	}
	inline void print_list(int x)
	{
		int i=x;
		do{
			printf("%d->",i);
			i=D[i];
		}while(i!=x);
		puts("");
	}
	inline void print(int x)
	{
		int i=x;
		do{
			print_list(i);
			puts("|");
			i=R[i];
		}while(i!=x);
		puts("");
		puts("");
	}

当然,你非得点行列写单独操作我也不拦你。

        inline void remove_point(int x)
	{
		D[U[x]]=D[x];
		U[D[x]]=U[x];
		R[L[x]]=R[x];
		L[R[x]]=L[x];
	}
	inline void resume_point(int x)
	{
		D[U[x]]=x;
		U[D[x]]=x;
		R[L[x]]=x;
		L[R[x]]=x;
	}
	inline void remove_line(int x)
	{
		int i=x;
		do{
			U[D[i]]=U[i];
			D[U[i]]=D[i];
			i=R[i];
		}while(i!=x);
	}
	inline void resume_line(int x)
	{
		int i=x;
		do{
			U[D[i]]=i;
			D[U[i]]=i;
			i=L[i];
		}while(i!=x);
	}
	inline void remove_list(int x)
	{
		int i=x;
		do{
			R[L[i]]=R[i];
			L[R[i]]=L[i];
			i=D[i];
		}while(i!=x);
	}
	inline void resume_list(int x)
	{
		int i=x;
		do{
			R[L[i]]=i;
			L[R[i]]=i;
			i=U[i];
		}while(i!=x);
	}

然后数据我就不贴了,直接去看3740discuss吧,有两组。

时间: 2024-10-20 13:29:44

【POJ3740】Easy Finding DLX(Dancing Links)精确覆盖问题的相关文章

hust 1017 dancing links 精确覆盖模板题

最基础的dancing links的精确覆盖题目 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 6 using namespace std; 7 #define N 1005 8 #define MAXN 1000100 9 10 struct DLX{ 11 int n , m , size;//size表示当前dlx表中有多少

ZOJ 3209 Treasure Map (Dancing Links 精确覆盖 )

题意 :  给你一个大小为 n * m 的矩形 , 坐标是( 0 , 0 ) ~ ( n , m )  .然后给你 p 个小矩形 , 坐标是( x1 , y1 ) ~ ( x2 , y2 ) , 你选择最小的几个矩形 , 使得这些矩形可以覆盖整个矩形 , 并且互相不会重叠 .( n , m <= 30 ) 思路 : Dancing Links 的精确覆盖问题 . 我们将 n * m 的矩形分成 n * m 个小正方形 ,那么我们只要保证每个小正方形被覆盖且只被覆盖一次即可 . 那么列表示每个小正

hihoCoder #1321 : 搜索五?数独 (Dancing Links ,精确覆盖)

hiho一下第102周的题目. 原题地址:http://hihocoder.com/problemset/problem/1321 题意:输入一个9*9数独矩阵,0表示没填的空位,输出这个数独的答案. 提示已经讲解的很清楚了.稍微整理下思路.最后附AC代码. 一.Dancing Links解决精确覆盖问题.      1.精确覆盖问题         给定一个n行,m列的01矩阵.从中选择若干行使得每一列有且恰好只有一个1. 例如: 答案是选择2,3,4行. 2.DancingLinks求解精确

[ACM] ZOJ 3209 Treasure Map ( Dancing Links 精确覆盖,矩形覆盖)

Treasure Map Time Limit: 2 Seconds      Memory Limit: 32768 KB Your boss once had got many copies of a treasure map. Unfortunately, all the copies are now broken to many rectangular pieces, and what make it worse, he has lost some of the pieces. Luck

HDU5046 Airport dancing links 重复覆盖+二分

这一道题和HDU2295是一样 是一个dancing links重复覆盖解决最小支配集的问题 在给定长度下求一个最小支配集,只要小于k就行 然后就是二分答案,每次求最小支配集 只不过HDU2295是浮点,这里是整数 我写的一个比较暴力 #include<cstdio> #include<cstring> #include<queue> #include<cstdlib> #include<algorithm> #include<vector

最新版dlx模板(精确覆盖+重复覆盖)

以前的代码太挫了,重新整理dlx,学习HH把精确覆盖,重复覆盖整合在一起. 代码: struct DLX{ const static int maxn=20010; #define FF(i,A,s) for(int i = A[s];i != s;i = A[i]) int L[maxn],R[maxn],U[maxn],D[maxn]; int size,col[maxn],row[maxn],s[maxn],H[maxn]; bool vis[70]; int ans[maxn],cnt;

hdu 1426 Sudoku Killer ( Dancing Link 精确覆盖 )

利用 Dancing Link 来解数独 具体的可以看    lrj 的训练指南 和 < Dancing Links 在搜索中的应用 >这篇论文 Dancing Link 来求解数独 , 是通过求解精确覆盖 精确覆盖就是给出一个 01 矩阵 , 要求我们选择一些行 , 使得每一列有且仅有一个 1 对于数独问题 , 行就是我们的选择 , 即在第 i 行 第 j 列 放上 数字 k , 所以我们最多有 i * j * k 中选择 如果某些位置( x , y  )已经放了数字 a , 那么我们的选择

HDU 3335 Divisibility dancing links 重复覆盖

分析: dlx重复覆盖的巧用,重复覆盖的原理恰好符合本题的筛选方式,即选择一个数后,该数的倍数或约数可以保证在之后的搜索中不会被选择 于是修改一下启发函数,求解最大的重复覆盖即可. 其实不一定不被选择,只是选择以后,要么达不成目标,要不达到目标,也不如不选择更优 举下面的例子 3 2 3 6 答案一看就是 2 初始的dancing links的表示是这样的 2   3   6 2    1   0   1 3    0   1   1 6    1   1   1 然后肯定先选第一列进行删 删

POJ 3740 Easy Finding DLX

题意:给你一个0,1矩阵 ,求精确覆盖 解题思路: DLX 解题代码: 1 // File Name: poj3740.cpp 2 // Author: darkdream 3 // Created Time: 2014年10月04日 星期六 20时06分31秒 4 5 #include<vector> 6 #include<list> 7 #include<map> 8 #include<set> 9 #include<deque> 10 #i