bzoj 3140: [Hnoi2013]消毒 洛谷P3231

本文解析洛谷P3231消毒问题的算法解决方案,通过二进制枚举最小维度,结合二分图匹配解决棋盘消毒问题。详细介绍了预处理、图构建、深度优先搜索及主函数实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://www.luogu.com.cn/problem/P3231

辣鸡bzoj卡常,洛谷过了

首先由于取min值的花费,相当于就是直接选择某一维度为1的去消毒了。

n*m*h<=5000,必有一个很小,那么我们对最小的一维进行2进制枚举,把这一维的哪些部分直接一个平面全部删掉,然后这一维就不管了,剩下的两维就是经典的棋盘问题的二分图匹配方法了,只能删除一整行或者一整列,最少需要多少步,最小点覆盖=最大匹配。

#include<bits/stdc++.h>
using namespace std;
 
const int maxl=5010;
 
int n,m,h,tot,cnt,id,len,mi,mx,ans;
int ehead[maxl],match[maxl];
struct node
{
    int x,y,z;
}a[maxl];
struct ed
{
    int to,nxt;
}e[maxl*maxl];
bool vis[maxl],in[maxl];
 
inline void prework()
{
    scanf("%d%d%d",&n,&m,&h);
	tot=0;int x;
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=m;++j)
            for(register int k=1;k<=h;++k)
            {
            	scanf("%d",&x);
            	if(x)
                	a[++tot]=node{i,j,k}; 
			}
    if(n<=m && n<=h) id=1,mi=n,len=1<<n,mx=max(m,h);
    else if(m<=n && m<=h)
    { 
        id=2;len=1<<m;mi=m;mx=max(n,h);
        for(register int i=1;i<=tot;++i)
            swap(a[i].x,a[i].y);
        swap(n,m);
    }
    else
    { 
        id=3;len=1<<h;mi=h;mx=max(n,m);
        for(register int i=1;i<=tot;++i)
            swap(a[i].z,a[i].x);
        swap(n,h);
    }
}
 
inline void add(int u,int v)
{
    e[++cnt].to=v;e[cnt].nxt=ehead[u];ehead[u]=cnt;
}
 
inline void build()
{
    cnt=0;
    memset(ehead,0,sizeof(int)*(m+1));
    memset(match,0,sizeof(int)*(h+1));
    for(register int i=1;i<=tot;++i)
    if(!in[a[i].x-1])
        add(a[i].y,a[i].z);
}
 
inline bool dfs(int u)
{
    int v;
    for(register int i=ehead[u];i;i=e[i].nxt)
    {
        v=e[i].to;
        if(!vis[v])
        {
            vis[v]=true;
            if(!match[v] || dfs(match[v]))
            {
                match[v]=u;
                return true;
            }
        }
    }
    return false;
}
 
inline void mainwork()
{
    int sum;ans=2e9;
    for(register int s=0;s<len;s++)
    {
        sum=0;
        for(int i=0;i<mi;++i)
        {
            in[i]=(s>>i)&1;
            if(in[i]) sum++;
        }
        build();
        for(register int i=1;i<=m && sum<ans;++i)
        {
            memset(vis,false,sizeof(bool)*(h+1));
            if(dfs(i))
                sum++;
        }
        ans=min(ans,sum);
    }
}
 
inline void print()
{
    printf("%d\n",ans);
}
 
int main()
{
    int t;
    scanf("%d",&t);
	for(int i=1;i<=t;i++)
    {
        prework();
        mainwork();
        print();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值