poj 3740 DLX(精确覆盖)

题意:经典的精确覆盖问题。

思路:精确覆盖问题是NPC的,用DLX能够比较有效的搜索。写完两个数独再写这个就不难了(实际上这个是原始问题,数独只是DLX的应用)。

#include <cstdio>
#include <cstring>
#define N 16
#define M 300
struct node{
    int l,r,u,d,x,y;
}p[N*M+M+5];
int s[N+5][M+5];
int n,m,top,cnt[M];
void init(){
    int i;
    memset(cnt, 0, sizeof(cnt));
    for(i = 0;i<=m;i++){
        p[i].l = i-1;
        p[i].r = i+1;
        p[i].u = p[i].d = i;
    }
    p[0].l = m;
    p[m].r = 0;
    top = m;
}
void add(int l,int i,int j){
    p[++top].x = i;
    p[top].y = j;
    p[top].l = l;
    p[p[top].l].r = top;
    p[top].u = p[j].u;
    p[p[top].u].d = top;
    p[j].u = top;
    p[top].d = j;
    cnt[j]++;
}
void del(int c){
    int i,j;
    p[p[c].l].r = p[c].r;
    p[p[c].r].l = p[c].l;
    for(i = p[c].d;i!=c;i=p[i].d){
        for(j = p[i].r;j!=i;j=p[j].r){
            p[p[j].u].d = p[j].d;
            p[p[j].d].u = p[j].u;
            cnt[p[j].y]--;
        }
    }
}
void re(int c){
    int i,j;
    p[p[c].l].r = c;
    p[p[c].r].l = c;
    for(i = p[c].d;i!=c;i=p[i].d)
        for(j = p[i].r;j!=i;j=p[j].r){
            p[p[j].u].d = j;
            p[p[j].d].u = j;
            cnt[p[j].y]++;
        }
}
int dfs(){
    int min=0x3fffffff,c=0,i,j;
    if(!p[0].r)
        return 1;
    for(i = p[0].r;i;i=p[i].r)
        if(cnt[i] < min){
            c = i;
            min = cnt[i];
        }
    del(c);
    for(i = p[c].d;i!=c;i=p[i].d){
        for(j = p[i].r;j!=i;j=p[j].r)
            del(p[j].y);
        if(dfs())
            return 1;
        for(j = p[i].l;j!=i;j=p[j].l)
            re(p[j].y);
    }
    re(c);
    return 0;
}
int main(){
    while(scanf("%d %d",&n,&m)!=EOF){
        int i,j,first;
        init();
        for(i = 1;i<=n;i++){
            s[i][0] = 0;//保存一行中1的个数
            for(j = 1;j<=m;j++){
                scanf("%d",&s[i][j]);
                if(s[i][j])
                    s[i][0]++;
            }
        }
        for(i = 1;i<=n;i++)
            for(j = first = 1;j<=m;j++)
                if(s[i][j]){
                    if(first){
                        first = 0;
                        add(top+s[i][0], i, j);
                    }else
                        add(top, i, j);
                }
        if(dfs())
            printf("Yes, I found it\n");
        else
            printf("It is impossible\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值