poj3740 Easy Finding 精确覆盖 舞蹈链/dfs

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  AA 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  MN ( 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

题意:能否选出一些行使每列只有一个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;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值