avatar
fireworks99
keep hungry keep foolish

FZU 1686 神龙的难题(DLX repeat cover)

Deacription

N x M的01矩阵,给出a、b,表示一次性可以将a行b列的小矩阵内的数字全变成0,问最终使全图为0需要几步

建模

把每个1视为列,每一种覆盖方式视为行

最少需要拿出多少行,使得每列都含有1——重复覆盖

Code

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX_V = 100005;///数组开小可能超时


int n, m, length, width, res, tot;
int cnt[255];///第i列1元素的个数
int L[MAX_V], R[MAX_V], U[MAX_V], D[MAX_V];
int Head[255];///第i行第一个1元素的ID,意义同列标元素
int Row[MAX_V], Col[MAX_V];///记录id所在行列
int ans[255], mp[20][20];
bool vis[255];

void init()
{
    for(int i = 0; i <= tot; ++i)///初始化Head及tot个列标元素
    {
        cnt[i] = 0;
        L[i] = i - 1;
        R[i] = i + 1;
        U[i] = i;
        D[i] = i;
//        Row[i] = 0;
//        Col[i] = i;
    }

    L[0] = tot;
    R[tot] = 0;

    memset(Head, -1, sizeof(Head));
}

void link(int row, int col, int id)
{
    Row[id] = row;
    Col[id] = col;

    U[id] = U[col];///我向上
    D[id] = col;///我向下
    D[ U[col] ] = id;///我上 下我
    U[col] = id;///我下 上我

    if(Head[row] == -1)
        Head[row] = L[id] = R[id] = id;
    else
    {
        L[id] = L[ Head[row] ];///我向左
        R[id] = Head[row];///我向右
        R[ L[ Head[row] ] ] = id;///我左 右我
        L[ Head[row] ] = id;///我右 左我
    }

    cnt[col]++;
}

///具体删除列,没有删除行(这一列有1了,我以后无需参考此列选行)
void abandon(int id)
{
    for(int i = D[id]; i != id; i = D[i])///空了id,便于后续删除列
        L[ R[i] ] = L[i], R[ L[i] ] = R[i];
}

void resume(int id)
{
    for (int i = D[id]; i != id; i = D[i])///空了id(因为上面就没abandon)
        L[ R[i] ] = R[ L[i] ] = i;
}

int h()
{
    int num = 0;
    memset(vis, 0, sizeof(vis));
    for(int i = R[0]; i != 0; i = R[i])
    {
        if(vis[i])
            continue;
        num++;
        for(int j = D[i]; j != i; j = D[j])
            for(int k = R[j]; k != j; k = R[k])
                vis[ Col[k] ] = 1;
    }
    return num;///当前矩阵可拆解次数
}


void Dance(int deep)
{
    if(deep + h() >= res)///估价剪枝
        return ;

    if(R[0] == 0)///准出口(每次abandon都会ban掉列标元素)
    {
        if(deep < res)
            res = deep;
        return;
    }

    ///找到1的数目最少的一列  将列标元素存在c里
    int c = R[0];
    for(int i = R[0]; i != 0; i = R[i])
        if(cnt[i] < cnt[c])
            c = i;

    ///对于c列1元素所在行,遍历每行,看看选择哪一行合适
    ///最少含1列,1所在行必然不被同时选
    for(int i = D[c]; i != c; i = D[i])
    {
        ans[deep] = Row[i];///选当前行

        abandon(i);///我选这个1,后续无需参考此列(删除)
        for(int j = R[i]; j != i; j = R[j])
            abandon(j);///删除1元素所在行上 1元素所在列

        Dance(deep + 1);

        for(int j = R[i]; j != i; j = R[j])
            resume(j);
        resume(i);
    }
}

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        tot = 0;
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j)
            {
                scanf("%d", &mp[i][j]);
                if(mp[i][j])
                    mp[i][j] = ++tot;
            }
        int id = tot, block = 0;
        init();
        scanf("%d%d", &length, &width);
        for(int i = 1; i + length - 1 <= n; ++i)
            for(int j = 1; j + width - 1 <= m; ++j)
            {
                block++;
                for(int k = i; k <= i + length - 1; ++k)
                    for(int l = j; l <= j + width - 1; ++l)
                        if(mp[k][l])
                            link(block, mp[k][l], ++id);
            }
        res = 0x3f3f3f3f;///不要自以为是随便搞
        Dance(0);
        cout << res << '\n';
    }
    return 0;
}
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy