fzu 1686 神龙的难题 重复覆盖+DLX 用最少的子矩阵覆盖矩阵没所有的1

该博客介绍了如何利用 Dancing Links (DLX) 算法解决一个数学问题——找到覆盖矩阵所有1的最少子矩阵数量。通过实例展示了如何将问题转化为图并应用DLX算法进行求解,涉及矩阵操作和图论概念。
摘要由CSDN通过智能技术生成

Problem Description

这是个剑与魔法的世界.英雄和魔物同在,动荡和安定并存.但总的来说,库尔特王国是个安宁的国家,人民安居乐业,魔物也比较少.但是.总有一些魔物不时会进入城市附近,干扰人民的生活.就要有一些人出来守护居民们不被魔物侵害.魔法使艾米莉就是这样的一个人.她骑着她的坐骑,神龙米格拉一起消灭干扰人类生存的魔物,维护王国的安定.艾米莉希望能够在损伤最小的前提下完成任务.每次战斗前,她都用时间停止魔法停住时间,然后米格拉他就可以发出火球烧死敌人.米格拉想知道,他如何以最快的速度消灭敌人,减轻艾米莉的负担.

 Input

数据有多组,你要处理到EOF为止.每组数据第一行有两个数,n,m,(1<=n,m<=15)表示这次任务的地区范围. 然后接下来有n行,每行m个整数,如为1表示该点有怪物,为0表示该点无怪物.然后接下一行有两个整数,n1,m1 (n1<=n,m1<=m)分别表示米格拉一次能攻击的行,列数(行列不能互换),假设米格拉一单位时间能发出一个火球,所有怪物都可一击必杀.

 Output

输出一行,一个整数,表示米格拉消灭所有魔物的最短时间.

 Sample Input

4 41 0 0 10 1 1 00 1 1 01 0 0 12 24 4 0 0 0 00 1 1 00 1 1 00 0 0 02 2

 Sample Output

41

//

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define eps 1e-8
#define N 550
#define V 360000
int n,m;//n行 m列
int L[V],R[V];
int D[V],U[V];
int C[V];
int S[N],H[N];
int ak,size;//ak 最少多少行可以覆盖所有列(可重复)
void Link(int r,int c)
{
    S[c]++;C[size]=c;
    U[size]=U[c];D[U[c]]=size;
    D[size]=c;U[c]=size;
    if(H[r]==-1) H[r]=L[size]=R[size]=size;
    else
    {
        L[size]=L[H[r]];R[L[H[r]]]=size;
        R[size]=H[r];L[H[r]]=size;
    }
    size++;
}
void remove(int c)
{
    int i;
    for(i=D[c];i!=c;i=D[i])
        L[R[i]]=L[i],R[L[i]]=R[i];
}
void resume(int c)
{
    int i;
    for(i=U[c];i!=c;i=U[i])
        L[R[i]]=R[L[i]]=i;
}
int h()
{
    int i,j,k,count=0;
    bool visit[N];
    memset(visit,0,sizeof(visit));
    for(i=R[0];i;i=R[i])
    {
        if(visit[i]) continue;
        count++;
        visit[i]=1;
        for(j=D[i];j!=i;j=D[j])
        {
            for(k=R[j];k!=j;k=R[k])
                visit[C[k]]=1;
        }
    }
    return count;
}
void Dance(int k)
{
    int i,j,c,Min,ans;
    ans=h();
    if(k+ans>=ak) return;
    if(!R[0])
    {
        if(k<ak) ak=k;
        return;
    }
    for(Min=N,i=R[0];i;i=R[i])
        if(S[i]<Min) Min=S[i],c=i;
    for(i=D[c];i!=c;i=D[i])
    {
        remove(i);
        for(j=R[i];j!=i;j=R[j])
            remove(j);
        Dance(k+1);
        for(j=L[i];j!=i;j=L[j])
            resume(j);
        resume(i);
    }
    return;
}
int a[30][30];
int hash[30][30];
int mat[300][300];
int main()
{
    int tn,tm;
    while(scanf("%d%d",&tn,&tm)==2)
    {
        for(int i=1;i<=tn;i++) for(int j=1;j<=tm;j++) scanf("%d",&a[i][j]);
        int ln,lm;scanf("%d%d",&ln,&lm);
        //所有小矩阵为行,所有怪物位置为列 构图+DLX
        //列之间不能有空列 即不能直接按照(i-1)*tm+j的位置来安排怪物
        n=m=0;//n 行数 m边数(怪物位置)
        for(int i=1;i<=tn;i++) for(int j=1;j<=tm;j++)
        if(a[i][j]) hash[i][j]=++m;
        memset(mat,0,sizeof(mat));
        for(int i=1;i+ln-1<=tn;i++)
        {
            for(int j=1;j+lm-1<=tm;j++)//小矩阵
            {
                ++n;
                for(int k=i;k<=i+ln-1;k++)//小矩阵内怪物
                {
                    for(int l=j;l<=j+lm-1;l++)
                    {
                        if(a[k][l]) mat[n][hash[k][l]]=1;
                    }
                }
            }
        }
        for(int i=0;i<=m;i++)
        {
            S[i]=0;
            U[i]=D[i]=i;
            L[i+1]=i;R[i]=i+1;
        }R[m]=0;
        memset(H,-1,sizeof(H));
        size=m+1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(mat[i][j]) Link(i,j);
            }
        }
        ak=N;
        Dance(0);
        printf("%d\n",ak);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值