P3231 [HNOI2013]消毒

洛谷传送门

Solution

先考虑二维

对于一个点 \((x,y)\) ,要么是在第 \(x\) 列被消毒,要么是在第 \(y\) 列被消毒,考虑二分图匹配,左边是列,右边是行,求最小点覆盖即可。

回到三维

显然三分图匹配是不可的,所以要看看别的东西。

题目中给了 \(abc\leq 5000\) ,也就是 \(\min\{a,b,c\}\leq \sqrt[3] {5000}\approx 17\) ,所以可以枚举最小的一维,判断是将一层全部消掉还是留下,然后再将剩下的拍成一个二维平面(或者说,将枚举的那一位坐标去掉),按上面二维方法求解即可。

Code

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
struct lsqy{
	int next,to;
}q[5010];
int a,b,c,ys[5101];
int dir[4][5101],tot; 
int match[5001],vis[5100],head[5101],cnt;
int ans=2147483644;
void add(int x,int y)
{
	q[++cnt].next=head[x];
	head[x]=cnt;
	q[cnt].to=y;
}
bool dfs(int u)
{
	for(int i=head[u];i;i=q[i].next)
	{
		int v=q[i].to;
		if(!vis[v])
		{
			vis[v]=1;
			if(dfs(match[v])||!match[v])
			{
				match[v]=u;
				return 1;
			}
		}
	}
	return 0;
}
void work(int s)
{
	for(int i=1;i<=b;++i)
	  head[i]=0;
	for(int j=1;j<=c;++j)
	  match[j]=0;
	cnt=0;
	int con=0;
	//memset(ys,0,sizeof(ys));
    for(int i=1;i<=a;++i)
	  if((1<<(i-1))&s) ys[i]=0,con++;
	    else ys[i]=1;
    for(int i=1;i<=tot;++i)
      if(ys[dir[1][i]])
        add(dir[2][i],dir[3][i]);
    for(int i=1;i<=b;++i)
    {
      for(int j=1;j<=c;++j)
        vis[j]=0;
      //memset(vis,0,sizeof(vis));
	  if(dfs(i)) con++;   
	}
	ans=min(ans,con); 
} 
int main()
{
	int T_T,minn;
	scanf("%d",&T_T);
	while(T_T--)
	{
		tot=0;
		minn=ans=2147483644;
		scanf("%d%d%d",&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;
		      	scanf("%d",&x);
		      	if(!x) continue;
		      	tot++;
				dir[1][tot]=i;
		      	dir[2][tot]=j;
		      	dir[3][tot]=k;
			  }
		  if(minn=b) swap(b,a),swap(dir[1],dir[2]);
		    else if(minn=c)  swap(c,a),swap(dir[1],dir[3]);
		for(int i=0;i<=(1<<a)-1;++i)
		  work(i);
		printf("%d\n",ans);
	}
	
 } 

感谢bored的精彩代码

posted @ 2020-11-05 15:59  jasony_sam  阅读(64)  评论(0编辑  收藏  举报