luogu P3231 消毒
这是一道二分图匹配的题目。
我一开始想了很久可是毫无思路,只发现了一个性质:对于长宽高三个维度\((A,B,C)\),我们每次选择的消毒范围必然有两维是要选满的(因为是要取\(min\),所以这样取一定是最优的)。
然后就不会了,点击查看题解。
首先考虑二维的情况:在一个\(n\)行\(m\)列的矩阵里有若干个点,每次你可以覆盖一行或者一列,操作一次的费用为\(1\),求覆盖所有点的最小费用。
我比较菜所以发现这个都不会做。。。
做法是在二分图左边放\(n\)个点,右边放\(m\)个点,对于坐标为\((x,y)\)的一个点,将其左边的第\(x\)号点和右边的第\(y\)号点相连,最后最大匹配就是答案。
考虑为什么这样做的对的:显然一个点\((x,y)\)要么被第\(x\)行覆盖要么被第\(y\)列覆盖,所以是我们建出来图的最小点覆盖,而最小点覆盖=最大匹配。
然后再考虑本题,发现并没有什么三分图匹配(反正我是不会),同时发现\(A \times B \times C\leq5000\),也就是说\(A\leq \sqrt[3]{5000} \leq 18\)(假设\(A \leq B \leq C\)),所以我们可以拿出来一维暴搜它要不要选,然后就被拍扁成二维的形式啦。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=200009,INF=1<<30;
queue <int> q;
int A,B,C,S,T,a[N],b[N],c[N],s[4][N],qwq,ans,tmp,head[N],cnt,cnmb[N];
int dep[N],cur[N];
struct Edge
{
int nxt,to,w;
}g[N*2];
int read()
{
char c=getchar();
int x=0;
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
x=x*10+c-'0',c=getchar();
return x;
}
void add(int from,int to,int w)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
g[cnt].w=w;
head[from]=cnt;
}
void ADD(int from,int to,int w)
{
add(from,to,w),add(to,from,0);
}
void clear()
{
for (int i=0;i<=T;i++)
dep[i]=-1,cur[i]=head[i];
}
bool bfs()
{
clear(),q.push(S),dep[S]=0;
while(!q.empty())
{
int x=q.front();q.pop();
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(dep[v]!=-1||g[i].w<=0)
continue;
dep[v]=dep[x]+1;
q.push(v);
}
}
return dep[T]!=-1;
}
int dfs(int x,int Min)
{
if(x==T)
return Min;
int flow=0;
for (int &i=cur[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(dep[v]!=dep[x]+1||g[i].w<=0)
continue;
int tmp=dfs(v,min(Min-flow,g[i].w));
flow+=tmp,g[i].w-=tmp,g[i^1].w+=tmp;
if(flow==Min)
break;
}
return flow;
}
int Dinic()
{
int ans=0;
while(bfs())
ans+=dfs(S,INF);
return ans;
}
void fuck(int x)
{
cnt=1;
int tmp=0;
for (int i=1;i<=A;i++)
if(1<<(i-1)&x)
tmp++,cnmb[i]=1;
else
cnmb[i]=0;
T=B+C+1;
for (int i=0;i<=T;i++)
head[i]=0;
for (int i=1;i<=qwq;i++)
if(!cnmb[a[i]])
ADD(b[i],c[i]+B,1);
for (int i=1;i<=B;i++)
ADD(S,i,1);
for (int i=1;i<=C;i++)
ADD(i+B,T,1);
tmp+=Dinic();
ans=min(ans,tmp);
}
void init()
{
A=read(),B=read(),C=read();
int x;
qwq=0;
for (int i=1;i<=A;i++)
for (int j=1;j<=B;j++)
for (int k=1;k<=C;k++)
{
x=read();
if(x)
s[1][++qwq]=i,s[2][qwq]=j,s[3][qwq]=k;
}
if(A>B)
swap(A,B),swap(s[1],s[2]);
if(A>C)
swap(A,C),swap(s[1],s[3]);
for (int i=1;i<=qwq;i++)
a[i]=s[1][i],b[i]=s[2][i],c[i]=s[3][i];
ans=INF;
}
void work()
{
int T;
T=read();
while(T--)
{
init();
for (int i=0;i<1<<A;i++)
fuck(i);
printf("%d\n",ans);
}
}
int main()
{
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!