FZU 1686 神龙的难题(重复覆盖问题)

神龙的难题

题目链接:

http://acm.fzu.edu.cn/problem.php?pid=1686

解题思路:

把每一个怪物编号,然后枚举矩形左上角,扫描矩形内的怪物编号建图,行为矩形的左上角编号,列为怪物编号,模型转化为选取

最少的矩形将列上的怪物覆盖,于是就转换为了DLX重复覆盖问题。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn = 15*15+10;
const int maxm = 15*15+10;
const int maxnode = maxn*maxm;
int a[20][20],id[20][20];

struct DLX{
    int n,m,len;
    int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
    int H[maxn];//行头结点
    int S[maxm];//每列有多少个结点
    int ansd,ans[maxn];//如果有答案,则选了ansd行,具体是哪几行放在ans[]数组里面,ans[0~ansd-1]

    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;
        len = m;//编号,每列都有一个头结点,编号1~m
        for(int i = 1; i <= n; i++)
            H[i] = -1;//每一行的头结点
    }

    void link(int r,int c){//第r行,第c列
        ++S[Col[++len]=c];//第len个节点所在的列为c,当前列的结点数++
        Row[len] = r;//第len个结点行位置为r
        D[len] = D[c];
        U[D[c]] = len;
        U[len] = c;
        D[c] = len;
        if(H[r] < 0)
            H[r] = L[len] = R[len] = len;
        else{
            R[len] = R[H[r]];
            L[R[H[r]]] = len;
            L[len] = H[r];
            R[H[r]] = len;
        }
    }

    void del(int c){
        for(int i = D[c]; i != c; i = D[i]){
            L[R[i]] = L[i];
            R[L[i]] = R[i];
        }
    }

    void resume(int c){
        for(int i = U[c]; i != c; i = U[i])
            L[R[i]] = R[L[i]] = i;
    }

    bool v[maxnode];
    int f(){
        int ret = 0;
        for(int c = R[0]; c != 0; c = R[c])
            v[c] = true;
        for(int c = R[0]; c != 0; c = R[c]){
            if(v[c]){
                ret++;
                v[c] = false;
                for(int i = D[c]; i != c; i = D[i]){
                    for(int j = R[i]; j != i; j = R[j]){
                        v[Col[j]] = false;
                    }
                }
            }
        }
        return ret;
    }

    void dance(int d){//递归深度
        if(d + f() >= ansd)
            return ;
        if(R[0] == 0){
            ansd = min(ansd,d);
            return ;
        }
        int c = R[0];
        for(int i = R[0]; i != 0; i = R[i]){
            if(S[i] < S[c])
                c = i;
        }
        for(int i = D[c]; i != c; i = D[i]){
            del(i);
            for(int j = R[i]; j != i; j = R[j])
                del(j);
            dance(d+1);
            for(int j = L[i]; j != i; j = L[j])
                resume(j);
            resume(i);
        }
    }
};

DLX head;

int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        int len = 0;
        memset(id,0,sizeof(id));
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                scanf("%d",&a[i][j]);
                if(a[i][j])
                    id[i][j] = (++len);
            }
        }
        head.init(n*m,len);
        len = 1;
        int n1,m1;
        scanf("%d%d",&n1,&m1);
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                for(int x = 0; x < n1 && i+x < n; x++){
                    for(int y = 0; y < m1 && j+y < m; y++){
                        if(id[i+x][j+y])
                            head.link(len,id[i+x][j+y]);
                    }
                }
                len++;
            }
        }
        head.ansd = INF;
        head.dance(0);
        printf("%d\n",head.ansd);
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值