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;
}