FZU Problem 1686 神龙的难题 —— 舞蹈链(DLX)重复覆盖

This way

题意:

现在有n*m的矩阵,每个格子内都有可能有一个敌人,你每次可以清掉r*c格子的敌人,问你最少需要几次才能清场

题解:

为了做这道题目我特地去注册了福州大学的OJ账号,毕竟这个神算法的题目确实不多。
我原来的板子还在用memset,现在已经改掉了,其他的应该没有啥问题了吧。
那么这道题很明显就是以所有格子为DLX的行,当然,我们可以先缩点,将r*c的矩形用最左上角的点表示
然后以每个怪作为DLX的列。
最后进行重复覆盖操作。

#include<iostream>
#include<stdio.h>
using namespace std;
const int N=200005;
int n,m;
int cnt;
int l[N],r[N],u[N],d[N],col[N],row[N];//每个点的左右上下指针,所在行列
int h[N];//每行的头结点
int s[N];//每列的节点数
int ansk[N];//选了那些集合
void init(int n,int m){//每行m个元素
    for(int i=0;i<=m;i++){
        r[i]=i+1;
        l[i]=i-1;
        u[i]=d[i]=i;
        s[i]=0;
    }
    for(int i=1;i<=n;i++)h[i]=-1;
    r[m]=0;
    l[0]=m;
    cnt=m+1;
}//初始化
void add(int R,int C){//R行C列插入点
    s[C]++;
    row[cnt]=R;
    col[cnt]=C;
    u[cnt]=C;
    d[cnt]=d[C];
    u[d[C]]=cnt;
    d[C]=cnt;
    if(h[R]==-1)h[R]=r[cnt]=l[cnt]=cnt;//该行没有点,直接加入
    else{
        r[cnt]=h[R];
        l[cnt]=l[h[R]];
        r[l[h[R]]]=cnt;
        l[h[R]]=cnt;
    }
    cnt++;
    //return;
}
void del(int c){
    for(int i=d[c];i!=c;i=d[i]){
        l[r[i]]=l[i],r[l[i]]=r[i];
    }
}//删除c列
void res(int c){
    for(int i=u[c];i!=c;i=u[i]){
        l[r[i]]=i,r[l[i]]=i;
    }
}//恢复c列
bool vis[N];
int leave(){
    int ans=0;
    int i,j,k;
    for(i=r[0];i!=0;i=r[i])vis[i]=0;
    for(i=r[0];i!=0;i=r[i]){
        if(vis[i]==0){
            vis[i]=1;
            ans++;
            for(j=d[i];j!=i;j=d[j]){
                for(k=r[j];k!=j;k=r[k])
                    vis[col[k]]=1;
            }
        }
    }
    return ans;
}//最优情况还要多少个集合
int bomb;
void dance(int deep){
//注意:这个剪枝只有在多解情况求最优解时下才用
    if(deep+leave()>=bomb)return;//剪枝
    if(r[0]==0){
        bomb=min(deep,bomb);
        return;
    }
    int c=r[0];
    register int i,j;
    for(i=r[0];i!=0;i=r[i])if(s[i]<s[c])c=i;//找到点最少的列
    for(i=d[c];i!=c;i=d[i]){
        ansk[deep]=row[i];
        del(i);
        for(j=r[i];j!=i;j=r[j])del(j);
        dance(deep+1);
        for(j=l[i];j!=i;j=l[j])res(j);
        res(i);
    }
    return;
}
int a[20][20],v[20][20],tot;
int main()
{
    while(~scanf("%d%d",&n,&m)){
        tot=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                scanf("%d",&a[i][j]),v[i][j]=a[i][j]?++tot:0;
        int r,c;
        scanf("%d%d",&r,&c);
        if(!tot){
            printf("0\n");
            continue;
        }
        init((n-r+1)*(m-c+1),tot);
        bomb=(n+r-1)/r;
        bomb*=(m+c-1)/c;
        tot=0;
        for(int i=0;i<=n-r;i++){
            for(int j=0;j<=m-c;j++){
                int cas=++tot;
                for(int k=i;k<i+r;k++)
                    for(int l=j;l<j+c;l++)
                        if(a[k][l])
                            add(cas,v[k][l]);
            }
        }
        dance(0);
        printf("%d\n",bomb);
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/tianyizhicheng/article/details/107667452