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;
}
posted @ 2020-04-14 15:46  With_penguin  阅读(116)  评论(0编辑  收藏  举报