习题: [HNOI2013]消毒(匈牙利)
题目
思路
我们先考虑低维的情况
一维
这应该有手就行
可惜我没有手
二维
我们注意到,我们肯定都是一次性将一次一行或者一列直接覆盖完全,这一定是最优的方法
之后我们观察一个矩阵,其只需要\(min(x,y)\)条线段就可以覆盖完全,这刚好就是题目中所提到的代价计算方式
然后就可以转换成为最小点覆盖的问题
三维
三维的情况也有点类似,我们每一次一定是将一个面删去,
然后你会发现不会三分图匹配这种玩意,所以考虑暴力枚举一维,剩下的两维二分图匹配即可
代码
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
using namespace std;
vector<int> g[5005];
int T;
int a,b,c;
int minn,tot;
int dir[4][5005],ans;
int mat[5005],vis[5005];
int used[5005];
void init()
{
ans=(1<<30);
tot=0;
memset(dir,0,sizeof(dir));
}
bool dfs(int u,int tag)
{
if(vis[u]==tag)
return 0;
vis[u]=tag;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(mat[v]==0||dfs(mat[v],tag))
{
mat[v]=u;
return 1;
}
}
return 0;
}
void solve(int s)
{
int ret=0;
for(int i=1;i<=tot;i++)
g[i].clear();
memset(mat,0,sizeof(mat));
for(int i=1;i<=a;i++)
{
if(s&(1<<(i-1)))
{
ret++;
used[i]=0;
}
else
used[i]=1;
}
for(int i=1;i<=tot;i++)
if(used[dir[1][i]])
g[dir[2][i]].push_back(dir[3][i]);
for(int i=1;i<=b;i++)
{
memset(vis,0,sizeof(vis));
if(dfs(i,i))
ret++;
}
ans=min(ans,ret);
}
void c_in()
{
init();
cin>>a>>b>>c;
minn=min(a,min(b,c));
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
for(int k=1;k<=c;k++)
{
int x;
cin>>x;
if(x)
{
tot++;
dir[1][tot]=i;
dir[2][tot]=j;
dir[3][tot]=k;
}
}
if(b==minn)
{
swap(a,b);
swap(dir[1],dir[2]);
}
else if(c==minn)
{
swap(a,c);
swap(dir[1],dir[3]);
}
/*for(int i=1;i<=3;i++)
{
for(int j=1;j<=tot;j++)
cout<<dir[i][j]<<' ';
cout<<'\n';
}*/
for(int i=0;i<(1<<a);i++)
solve(i);
cout<<ans<<'\n';
}
int main()
{
cin>>T;
for(int i=1;i<=T;i++)
c_in();
return 0;
}

浙公网安备 33010602011771号