http://poj.org/problem?id=3740
Easy Finding
| Time Limit: 1000MS | Memory Limit: 65536K | |
| Total Submissions: 16136 | Accepted: 4328 |
Description
Given a
M×
N matrix
A.
A
ij ∈ {0, 1} (0 ≤ i < M, 0 ≤ j < N), could you find some rows that let every cloumn contains and only contains one 1.
Input
There are multiple cases ended by
EOF. Test case up to 500.The first line of input is
M,
N (
M ≤ 16,
N ≤ 300). The next
M lines every line contains
N integers separated by space.
Output
For each test case, if you could find it output "Yes, I found it", otherwise output "It is impossible" per line.
Sample Input
3 3 0 1 0 0 0 1 1 0 0 4 4 0 0 0 1 1 0 0 0 1 1 0 1 0 1 0 0
Sample Output
Yes, I found it It is impossible
Source
POJ Monthly Contest - 2009.08.23, MasterLuo
题意:能否选出一些行使每列只有一个1。
题解:裸的精确覆盖问题,采用一种神奇的数据结构Dancing Links(舞蹈链),其实就是一个十字链表优化dfs,使得删除和恢复操作变的方便。
推荐一个详细的介绍:http://www.cnblogs.com/grenet/p/3145800.html ,讲的十分详细,照着看完就差不多可以理解了。
代码:
/**
* @author neko01
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
#define min3(a,b,c) min(a,min(b,c))
#define max3(a,b,c) max(a,max(b,c))
#define pb push_back
#define mp(a,b) make_pair(a,b)
#define clr(a) memset(a,0,sizeof a)
#define clr1(a) memset(a,-1,sizeof a)
#define dbg(a) printf("%d\n",a)
typedef pair<int,int> pp;
const double eps=1e-9;
const double pi=acos(-1.0);
const int INF=0x3f3f3f3f;
const int maxnode=50000;
const int N=20;
const int M=305;
struct DLX{
int n,m; //行列数
int sz; //节点数
int U[maxnode],D[maxnode],R[maxnode],L[maxnode]; //十字链表
int Row[maxnode],Col[maxnode]; //各节点行列编号
int S[M]; //当前所在列节点数
int H[N];
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;
}
R[m]=0;L[0]=m;
sz=m;
for(int i=1;i<=n;i++)
H[i]=-1;
}
void link(int r,int c)
{
++S[Col[++sz]=c];
Row[sz]=r;
//加入垂直链
//c<=>sz<=>D[c]
D[sz]=D[c];
U[D[c]]=sz;
U[sz]=c;
D[c]=sz;
//加入水平链
if(H[r]<0) H[r]=L[sz]=R[sz]=sz; //该行第一个节点需要构造
//H[r]<=>sz<=>R[H[r]]
else
{
R[sz]=R[H[r]];
L[R[H[r]]]=sz;
L[sz]=H[r];
R[H[r]]=sz;
}
}
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])
{
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;
}
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];
for(int j=R[i];j!=i;j=R[j]) //选择该行就要删除该行含有的列
remove(Col[j]);
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))
{
g.init(n,m);
int x;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
scanf("%d",&x);
if(x==1)
g.link(i+1,j+1);
}
}
if(g.dance(0))
puts("Yes, I found it");
else
puts("It is impossible");
}
return 0;
}
再附一个dfs的,数据也挺水,一点都没剪枝。,
/**
* @author neko01
*/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
#define min3(a,b,c) min(a,min(b,c))
#define max3(a,b,c) max(a,max(b,c))
#define pb push_back
#define mp(a,b) make_pair(a,b)
#define clr(a) memset(a,0,sizeof a)
#define clr1(a) memset(a,-1,sizeof a)
#define dbg(a) printf("%d\n",a)
typedef pair<int,int> pp;
const double eps=1e-9;
const double pi=acos(-1.0);
const int INF=0x3f3f3f3f;
const int N=18;
const int M=305;
int a[N][M];
int vis[M];
int n,m;
bool gao(int x)
{
for(int i=0;i<m;i++)
if(vis[i]+a[x][i]>1)
return false;
return true;
}
bool check()
{
for(int i=0;i<m;i++)
if(vis[i]==0)
return false;
return true;
}
bool dfs(int x)
{
if(check())
return true;
for(int i=x;i<n;i++)
{
if(gao(i))
{
for(int j=0;j<m;j++)
vis[j]+=a[i][j];
if(dfs(i+1)) return true;
for(int j=0;j<m;j++)
vis[j]-=a[i][j];
}
}
return false;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
clr(vis);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%d",&a[i][j]);
if(dfs(0))
puts("Yes, I found it");
else
puts("It is impossible");
}
return 0;
}
本文探讨了一种经典的组合数学问题——精确覆盖问题,并通过两种不同的方法进行求解。首先介绍了利用Dancing Links(舞蹈链)数据结构实现的高效求解算法,随后给出了另一种基于深度优先搜索(DFS)的解决方案。

被折叠的 条评论
为什么被折叠?



