FZU 1686 神龙的难题(DLX 重复覆盖)解题报告

2017-05-12 1,849 次浏览 解题报告

解题思路

首先将所有有怪物的坐标编号,作为 DLX 矩阵的列,然后将所有攻击区域以选定的某个顶点坐标编号,作为 DLX 矩阵的行。这样本题就转化成了求最少的行数(即攻击次数)使得所有列(即怪物)都被覆盖,而且由于题目允许同一个位置被多次攻击到,这里我们使用 DLX 的重复覆盖算法来解决这个问题。

参考代码

#include <cstdio>
#include <cstring>
using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXN = 15*15;
const int MAXM = 15*15;

struct DLX {
    int l, r, u, d;
    int row, col;
} dlx[(MAXN+1)*(MAXM+1)+MAXM+1];

int sz[MAXM+1], idx, head[MAXN+1], ans;
bool del[MAXM+1];

void Init(int n, int m) {
    for(int i=0; i<=m; ++i) {
        dlx[i].u = dlx[i].d = i;
        dlx[i+1].l = i;
        dlx[i].r = i+1;
        sz[i] = 0;
    }
    dlx[m].r = 0;
    dlx[0].l = m;
    idx = m+1;
    for(int i=1; i<=n; ++i) {
        head[i] = -1;
    }
}

void Insert(int r, int c) {
    sz[c]++;
    // U/D
    dlx[dlx[c].u].d = idx;
    dlx[idx].u = dlx[c].u;
    dlx[idx].d = c;
    dlx[c].u = idx;
    dlx[idx].row = r;
    dlx[idx].col = c;
    // L/R
    if(head[r] == -1)
        head[r] = dlx[idx].l = dlx[idx].r = idx;
    else {
        dlx[dlx[head[r]].l].r = idx;
        dlx[idx].l = dlx[head[r]].l;
        dlx[idx].r = head[r];
        dlx[head[r]].l = idx;
    }
    idx++;
}

void Remove(int c) {
    for(int i=dlx[c].d; i!=c; i=dlx[i].d) {
        dlx[dlx[i].r].l = dlx[i].l;
        dlx[dlx[i].l].r = dlx[i].r;
    }
}

void Resume(int c) {
    for(int i=dlx[c].u; i!=c; i=dlx[i].u) {
        dlx[dlx[i].r].l = dlx[dlx[i].l].r = i;
    }
}

int A() {
    memset(del, false, sizeof del);
    int ret = 0;
    for(int i=dlx[0].r; i; i=dlx[i].r) {
        if(!del[i]) {
            ret++;
            del[i] = true;
            for(int j=dlx[i].d; j!=i; j=dlx[j].d) {
                for(int k=dlx[j].r; k!=j; k=dlx[k].r) {
                    del[dlx[k].col] = true;
                }
            }
        }
    }

    return ret;
}

void Dance(int cnt) {
    if(cnt+A() >= ans) return;
    if(dlx[0].r == 0) {
        ans = cnt;
        return;
    }
    else {
        int minsz = INF, c;
        for(int i=dlx[0].r; i!=0; i=dlx[i].r) {
            if(sz[i] == 0) return;
            if(sz[i] < minsz) {
                minsz = sz[i];
                c = i;
                if(minsz == 1) break;
            }
        }
        for(int i=dlx[c].d; i!=c; i=dlx[i].d) {
            Remove(i);
            for(int j=dlx[i].r; j!=i; j=dlx[j].r) {
                Remove(j);
            }
            Dance(cnt+1);
            for(int j=dlx[i].l; j!=i; j=dlx[j].l) {
                Resume(j);
            }
            Resume(i);
        }
    }
}

int main(int argc, char const *argv[]) {
    int n, m, v, a[16][16], n1, m1;
    while(~ scanf("%d %d", &n, &m)) {
        int id = 0;
        for(int i=1; i<=n; ++i) {
            for(int j=1; j<=m; ++j) {
                scanf("%d", &v);
                if(v == 1) a[i][j] = ++id;
                else a[i][j] = 0;
            }
        }
        Init(n*m, id);
        scanf("%d %d", &n1, &m1);
        for(int i=1; i<=n-n1+1; ++i) {
            for(int j=1; j<=m-m1+1; ++j) {
                for(int k=i; k<=i+n1-1; ++k) {
                    for(int l=j; l<=j+m1-1; ++l) {
                        if(a[k][l]) Insert((i-1)*m+j, a[k][l]);
                    }
                }
            }
        }
        ans = INF;
        Dance(0);
        printf("%d\n", ans);
    }
    
    return 0;
}

bLue 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。
本文地址:https://dreamer.blue/blog/post/2017/05/12/fzu-1686.dream

还不快抢沙发

添加新评论