ZOJ 3209 Treasure Map(精确覆盖问题&舞蹈链)

题目链接[kuangbin带你飞]专题三 Dancing Links B - Treasure Map

题意

给一矩形和k个小矩形,问选取最小数量为多少的小矩形可以对大矩形进行精确覆盖。

思路

仍然是个模版题,把二维的n*m的大矩形看作是一维的n*m的一条线。k个小矩形同理,那么就转化成01矩阵精确覆盖的问题了。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>

using namespace std;

const int N = 1009;
const int MAX = 1000009;

int U[MAX], D[MAX], L[MAX], R[MAX];//数组模拟链表指向(上下左右)
int C[MAX], M[MAX];//节点所在列与行
int S[N];//储存每列的元素数量
int H[N];//行头指针
int ans;

void init(int n, int m)
{
    for(int i=0; i<=m; i++)
    {
        L[i+1] = i;
        R[i] = i+1;
        U[i] = D[i] = i;
        S[i] = 0;
    }
    for(int i=1; i<=n; i++)
        H[i] = -1;
    L[0] = m;
    R[m] = 0;
}

void link(int row, int col, int id)//将节点加入链表
{
    C[id] = col; M[id] = row;//记录行列
    U[id] = U[col]; D[U[col]] = id;//上下连接
    D[id] = col; U[col] = id;
    if(H[row] == -1)//左右连接(使用表头方便头插)
        H[row] = L[id] = R[id] = id;
    else
    {
        L[id] = L[H[row]]; R[L[H[row]]] = id;
        L[H[row]] = id; R[id] = H[row];
    }
    S[col]++;
}

void remove(int col)//删除列
{
    R[L[col]] = R[col];
    L[R[col]] = L[col];
    for(int i=D[col]; i!=col; i=D[i])
    {
        for(int j=R[i]; j!=i; j=R[j])
        {
            U[D[j]] = U[j];
            D[U[j]] = D[j];
            S[C[j]]--;
        }
    }
}

void resume(int col)//恢复列(先删的后恢复,后删的先恢复,所以跟remove反向操作)
{
    R[L[col]] = col;
    L[R[col]] = col;
    for(int i=U[col]; i!=col; i=U[i])
    {
        for(int j=L[i]; j!=i; j=L[j])
        {
            U[D[j]] = j;
            D[U[j]] = j;
            S[C[j]]++;
        }
    }
}

void dance(int k)
{
    if(ans!=-1 && k>=ans)
        return;
    if(!R[0])
    {
        ans = k;
        return;
    }
    int col = R[0];
    for(int i=R[0]; i!=0; i=R[i])
        if(S[i] < S[col])
            col = i;
    remove(col);
    for(int i=D[col]; i!=col; i=D[i])
    {
        for(int j=R[i]; j!=i; j=R[j])
            remove(C[j]);
        dance(k+1);
        for(int j=L[i]; j!=i; j=L[j])
            resume(C[j]);
    }
    resume(col);
}

int main()
{
    int n, m, k, T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%d", &n, &m, &k);
        init(k, n*m);
        int id = n*m+1;
        for(int i=1; i<=k; i++)
        {
            int x1, x2, y1, y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            for(int x=x1+1; x<=x2; x++)
                for(int y=y1+1; y<=y2; y++)
                    link(i, y+(x-1)*m, id++);
        }
        ans = -1;
        dance(0);
        printf("%d\n", ans);
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值