hihoCoder 1317 搜索四·跳舞链(DLX模板)

题目链接:http://hihocoder.com/problemset/problem/1317

 

题意:

小Ho最近遇到一个难题,他需要破解一个棋局。

棋局分成了n行,m列,每行有若干个棋子。小Ho需要从中选择若干行使得每一列有且恰好只有一个棋子。

 

来一个链接好复习https://www.cnblogs.com/grenet/p/3145800.html

跳舞链模板

 


#include<bits/stdc++.h>

using namespace std;
const int maxn=105;
const int maxm=105;
const int maxnode=105*105;
const int head=0;
typedef long long ll;
int L[maxnode],R[maxnode],U[maxnode],D[maxnode],Row[maxnode];
int C[maxnode],H[maxnode],coln[maxm],ans[maxn],vis[maxn];
int n,m,sz,len;
void init(){
    for(int i=0;i<=m;i++){
        coln[i]=0; U[i]=D[i]=i;
        L[i+1]=i;R[i]=i+1;
    }
    R[m]=0; L[0]=m;
    memset(H,-1,sizeof(H));
    sz=m+1;
}
void addLink(int r,int c){
    coln[c]++; C[sz]=c; Row[sz]=r;
    U[sz]=U[c]; D[U[c]]=sz;
    D[sz]=c; U[c]=sz;
    if(H[r]==-1) H[r]=L[sz]=R[sz]=sz;
    else{
        L[sz]=L[H[r]];  R[L[sz]]=sz;
        R[sz]=H[r]; L[H[r]]=sz;
    }
    sz++;
}
void Remove(int sz){
    L[R[sz]]=L[sz];  R[L[sz]]=R[sz];
    for(int i=D[sz];i!=sz;i=D[i]){
        for(int j=R[i];j!=i;j=R[j]){
            U[D[j]]=U[j]; D[U[j]]=D[j];
            coln[C[j]]--;
        }
    }
}
void Resume(int sz){
    for(int i=D[sz];i!=sz;i=D[i]){
        for(int j=R[i];j!=i;j=R[j]){
            U[D[j]]=j; D[U[j]]=j;
            coln[C[j]]++;
        }
    }
    L[R[sz]]=sz;  R[L[sz]]=sz;
}
int DLX(int k){
    //如果head的右边也是head 说明已经删除完毕
    if(R[head]==head){
        len=k;
        return 1;
    }
    int minn=maxn,minpos=-1;
    //找到当前还有1的数量最少的列进行选择 算是个优化了
    for(int i=R[0];i!=0;i=R[i]){
        if(minn>coln[i]){
            minn=coln[i];  minpos=i;
        }
    }
    Remove(minpos);
    //选择要删去的行并实时更新至ans中
    for(int i=D[minpos];i!=minpos;i=D[i]){
        ans[k]=Row[i];
        for(int j=R[i];j!=i;j=R[j]) {
            //选择点i所在的行作为覆盖c列的行
            //要删掉该行所覆盖的列
            Remove(C[j]);
        }
        if(DLX(k+1)) return 1;
        for(int j=L[i];j!=i;j=L[j]) Resume(C[j]);
    }
    Resume(minpos);
    return 0;
}
int main(){
    int T; cin>>T;
    while(T--){
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                int xx; scanf("%d",&xx);
                if(xx) addLink(i,j);
            }
        }
        int ans=DLX(0);
        if(ans) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值