bzoj3140: [Hnoi2013]消毒

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3140

思路:首先我们观察题目只需要使用min{x,y,z}单位的F试剂

那么我们如果选择一位长度为a,那其他两维直接取到最大即可

那么题目就相当于问最少切多少个面才能覆盖所有点

二维的很简单,直接二分图匹配即可(不会的见poj3041)

http://poj.org/problem?id=3041

三维的怎么办?

这时a*b*c<=5000就有用了

我们2^n枚举最小的一维每层切还是不切(不超过17)

不切的层再二分图匹配即可

复杂度有点坑,但还是可以接受的

千万不要作死用memset

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=5010,inf=1e9,maxm=100010;
using namespace std;
struct poi{int x,y,z;}p[maxn];
int mat[maxn],cnt,cas,pw[20],ans,vis[maxn],pre[maxm],now[maxn],son[maxm],tim,tot,a,b,c;
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
bool cmp(poi a,poi b){
	if (a.x!=b.x) return a.x<b.x;
	if (a.y!=b.y) return a.y<b.y;
	return a.z<b.z;
}

bool dfs(int x){
	for (int y=now[x];y;y=pre[y]){
		int v=son[y];
		if (vis[v]<tim){
			vis[v]=tim;
			if (!mat[v]||dfs(mat[v])) return mat[v]=x,1;
		}
	}
	return 0;
}

void work(int st){
	int res=0,n=b,m=c;tot=0;
	for (int i=0;i<a;i++) if (st&pw[i]) res++;
//	printf("%d\n",res);
	if (res>=ans) return;
	for (int i=1;i<=n;i++) now[i]=0;
	for (int i=1;i<=m;i++) mat[i]=0;
	for (int i=1;i<=cnt;i++) if (!(st&pw[p[i].x-1])) add(p[i].y,p[i].z);
	for (int i=1;i<=n;i++){
		tim++;
		if (dfs(i)) res++;
		if (res>=ans) return;
	}
	ans=res;
}

int main(){
	scanf("%d",&cas);
	pw[0]=1;for (int i=1;i<=19;i++) pw[i]=pw[i-1]*2;
	//for (int i=1;i<=19;i++) printf("pw %d\n",pw[i]);
	while (cas--){
		scanf("%d%d%d",&a,&b,&c),ans=1e9,cnt=0;
		for (int i=1,op;i<=a;i++) for (int j=1;j<=b;j++) for (int k=1;k<=c;k++){scanf("%d",&op);if (op) p[++cnt]=(poi){i,j,k};}
		if (b<a){swap(a,b);for (int i=1;i<=cnt;i++) swap(p[i].x,p[i].y);}
		if (c<a){swap(a,c);for (int i=1;i<=cnt;i++) swap(p[i].x,p[i].z);}
		sort(p+1,p+1+cnt,cmp);
		for (int i=0;i<pw[a];i++) work(i);
		printf("%d\n",ans);
	}
	return 0;
}


你可能感兴趣的:(bzoj3140: [Hnoi2013]消毒)