舞蹈链 (dancing links X)

舞蹈链算法,用于求解精确覆盖或者重复覆盖问题,目的是保证每一列的值最后为1或大于1,搜索中选用某一行后就将这一行以及它所解决的所有列删去,最后如果发现有列无法删去则失败,回溯并将之前删去的都恢复;如果最后发现所有列都被删去了,则说明问题解决。

它首先可以用来解决数独问题:转化为每行,每列,每个宫都有1~9,每格只有一个数字这个条件。是精确覆盖问题。

HDU3111题目链接

#include<bits/stdc++.h>
using namespace std;

const int N=350;
const int M=740;
const int inf=0x3f3f3f3f;

int m,n;
int u[M*N],d[M*N],r[M*N],l[M*N];
int cntc[N];
int c[M*N];
int head;
int op[M][N];
int res[M],idx;
int ans[10][10];

bool build(){
    int i,j,now,pre,first;
    idx=head=0;
    for(i=0;i<n;i++){
        r[i]=i+1;
        l[i+1]=i;
    }
    r[n]=0;
    l[0]=n;

    for(j=1;j<=n;j++){
        pre=j;
        cntc[j]=0;
        for(i=1;i<=m;i++){
            if(op[i][j]){
                now=i*n+j;
                c[now]=j;
                cntc[j]++;
                u[now]=pre;
                d[pre]=now;
                pre=now;
            }
        }
        u[j]=pre;
        d[pre]=j;
        if(cntc[j]==0)
            return false;
    }

    for(int i=1;i<=m;i++){
        pre=first=-1;
        for(j=1;j<=n;j++){
            if(op[i][j]){
                now=i*n+j;
                if(first==-1)
                    first=now;
                else{
                    r[pre]=now;
                    l[now]=pre;
                }
                pre=now;
            }
        }
        if(first!=-1){
            l[first]=pre;
            r[pre]=first;
        }
    }
    return true;
}

void remove(int col){
    int i,j;
    l[r[col]]=l[col];
    r[l[col]]=r[col];
    for(i=d[col];i!=col;i=d[i]){
        for(j=r[i];j!=i;j=r[j]){  //这一列的所有行都变为不可用,因为每列仅可覆盖一次
            u[d[j]]=u[j];
            d[u[j]]=d[j];
            cntc[c[j]]--;
        }
    }
}
void resume(int col){
    int i,j;
    r[l[col]]=col;
    l[r[col]]=col;
    for(i=d[col];i!=col;i=d[i]){
        for(j=r[i];j!=i;j=r[j]){
            u[d[j]]=j;
            d[u[j]]=j;
            cntc[c[j]]++;
        }
    }
}

bool dfs(){
    int i,j,col;
    if(r[head]==head)
        return true;
    int min=inf;
    for(i=r[head];i!=head;i=r[i]){
        if(cntc[i]<min){
            min=cntc[i];
            col=i;
        }
    }
    remove(col);
    for(i=d[col];i!=col;i=d[i]){
        res[idx++]=(i-1)/n;
        for(j=r[i];j!=i;j=r[j]){
            remove(c[j]);
        }
        if(dfs())
            return true;
        for(j=l[i];j!=i;j=l[j]){
            resume(c[j]);
        }
        idx--;
    }
    resume(col);
    return false;
}

void print(){
    int i,j,x,y,val;
    for(i=0;i<idx;i++){
        val=res[i]%9;
        if(val==0)
            val=9;
        int tmp=((res[i]-val)/9+1);
        y=tmp%9;
        if(y==0)
            y=9;
        x=(tmp-y)/9+1;
        ans[x][y]=val;
    }
    if(idx==0){
        cout<<"impossible"<<endl;
    }else{
        for(i=1;i<=9;i++){
            for(j=1;j<=9;j++)
                cout<<ans[i][j];
            cout<<endl;
        }
    }
}

int main(){
    int t;
    char s[10][10];
    char sep[10];
    cin>>t;
    for(int i=1;i<=t;i++){
        for(int i=1;i<=9;i++){
            scanf("%s",&s[i][1]);
        }
        if(i<t)
            scanf("%s",sep);
        memset(op,0,sizeof(op));

        for(int i=1;i<=9;i++){
            for(int j=1;j<=9;j++){
                int val=(i-1)*9+j;
                if(s[i][j]=='?'){
                    for(int k=1;k<=9;k++){
                        op[(val-1)*9+k][val]=1;
                        op[(val-1)*9+k][81+(i-1)*9+k]=1;
                        op[(val-1)*9+k][162+(j-1)*9+k]=1;
                        op[(val-1)*9+k][243+9*((i-1)/3*3+(j+2)/3-1)+k]=1;
                    }
                }else{
                    int k=s[i][j]-'0';
                    op[(val-1)*9+k][val]=1;
                    op[(val-1)*9+k][81+(i-1)*9+k]=1;
                    op[(val-1)*9+k][162+(j-1)*9+k]=1;
                    op[(val-1)*9+k][243+9*((i-1)/3*3+(j+2)/3-1)+k]=1;
                }
            }
        }
        n=324;
        m=729;
        build();
        dfs();
        print();
        if(i<t)
            cout<<"---"<<endl;
    }
    return 0;
}

 

接下来我们看另外一种问题,即重复覆盖问题,由于允许某一列的值累加大于1,故删除列的函数需要进行修改。之前我们删去某一列时,需要把这一列上所有的点对应的行都删去,因为我们已经选择了这一列,所以这些点只需要一个就够了;现在则没有这么强的限制,删去某一列时需要删掉这一列和其他列的连接,这一点是为了防止重复删去,因为还是要尽量避免选取某个值的位置重复的行。

HDU2295

#include<bits/stdc++.h>
using namespace std;

const int mx=55;

int r[mx*mx],l[mx*mx],u[mx*mx],d[mx*mx];
int c[mx*mx];
int n,m,k;
bool vis[mx];
int op[mx][mx];
int cntc[mx];
double eps=1e-7;
int head;

double city[mx][2],radar[mx][2];
double dis[mx][mx];
double cal(int i,int j){
    return sqrt((city[j][0]-radar[i][0])*(city[j][0]-radar[i][0])+\
                (city[j][1]-radar[i][1])*(city[j][1]-radar[i][1]));
}
bool build(){
    int i,j,pre,now,first;

    memset(cntc,0,sizeof(cntc));
    head=0;
    for(i=0;i<n;i++){
        r[i]=i+1;
        l[i+1]=i;
    }
    l[0]=n;
    r[n]=0;

    for(j=1;j<=n;j++){
        pre=j;
        for(i=1;i<=m;i++){
            if(op[i][j]){
                cntc[j]++;

                now=i*n+j;
                d[pre]=now;
                u[now]=pre;
                c[now]=j;

                pre=now;
            }
        }
        d[pre]=j;
        u[j]=pre;
        if(cntc[j]==0)
            return false;
    }

    for(i=1;i<=m;i++){
        pre=first=-1;
        for(j=1;j<=n;j++){
            if(op[i][j]){
                now=i*n+j;
                if(pre==-1){
                    first=now;  //不单独设行首,直接以第一个元素作哨兵
                }else{
                    r[pre]=now;
                    l[now]=pre;
                }
                pre=now;
            }
        }
        if(first!=-1){
            l[first]=pre;
            r[pre]=first;
        }
    }
    return true;
}

void remove(int col){
    int i;
    for(i=d[col];i!=col;i=d[i]){
        r[l[i]]=r[i];
        l[r[i]]=l[i];
    }
}
void resume(int col){
    int i;
    for(i=d[col];i!=col;i=d[i]){
        l[r[i]]=i;
        r[l[i]]=i;
    }
}

int h(){
    memset(vis,0,sizeof(vis));
    int i,j,col,res=0;
    for(col=r[head];col!=head;col=r[col]){
        if(!vis[col]){
            res++;
            for(i=d[col];i!=col;i=d[i]){
                for(j=r[i];j!=i;j=r[j]){
                    vis[c[j]]=1;
                }
            }
        }
    }
    return res;
}

bool dfs(int dep){
    if(r[head]==head)
        return true;
    if(h()+dep>k)       //剪枝
        return false;

    int i,j,col,min=0x3f3f3f3f;
    for(i=r[head];i!=head;i=r[i]){
        if(cntc[i]<min){
            min=cntc[i];
            col=i;
        }
    }

    for(i=d[col];i!=col;i=d[i]){
        remove(i);
        for(j=r[i];j!=i;j=r[j]){
            remove(j);
            cntc[c[j]]--;
        }
        if(dfs(dep+1))
            return true;
        for(j=l[i];j!=i;j=l[j]){
            resume(j);
            cntc[c[j]]++;
        }
        resume(i);
    }
    return false;
}

bool ok(){
    if(!build())
        return false;
    return dfs(0);
}

int main(){
    int i,j,t;
    cin>>t;
    while(t--){
        cin>>n>>m>>k;
        for(i=1;i<=n;i++){
            cin>>city[i][0]>>city[i][1];
        }
        for(j=1;j<=m;j++){
            cin>>radar[j][0]>>radar[j][1];
        }
        for(i=1;i<=m;i++){
            for(j=1;j<=n;j++){
                dis[i][j]=cal(i,j);
            }
        }
        double ub=1000,lb=0,mid;
        while(ub-lb>eps){
            mid=(ub+lb)/2;
            memset(op,0,sizeof(op));
            for(i=1;i<=m;i++){
                for(j=1;j<=n;j++){
                    if(mid>dis[i][j])
                        op[i][j]=1;
                }
            }
            if(ok()){
                ub=mid;
            }else
                lb=mid;
        }
        printf("%.6f\n",mid);
    }
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Absoler

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值