[BZOJ3140][网络流]HNOI2013:消毒

BZOJ3140

二维的情况:把所有x和y转成二分图,跑最大匹配即可
推广到三维:因为 a ∗ b ∗ c ≤ 5000 a*b*c\leq5000 abc5000,所以有一维必定小于17,则 2 17 2^{17} 217枚举这一维的状态,剩下的二维直接做就好了
注意memset会T

Code:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int cur=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {cur=(cur<<1)+(cur<<3)+(ch^48);ch=getchar();}
	return cur*f;
}
const int N=1e5+5,INF=1e9;
int vis[N<<1],head[N],nxt[N<<1],c[N<<1],tot=1;
int curr[N<<1];
inline void add(int x,int y,int z){
	vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;
	vis[++tot]=x;nxt[tot]=head[y];head[y]=tot;c[tot]=0;
}
int st[N];
int s,t;
inline bool bfs(){
	for(int i=s;i<=t;i++) st[i]=-1;
	queue<int>q;
	st[s]=1;q.push(s);
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=head[x];i;i=nxt[i])
			if(c[i] && st[vis[i]]==-1){
				st[vis[i]]=st[x]+1;
				q.push(vis[i]);
				if(vis[i]==t) return 1;
			} 
	}
	return 0;
}
int dfs(int v,int flow){
	if(v==t) return flow;
	int rest=flow,k;
	for(int &i=curr[v];i && rest;i=nxt[i])
		if(c[i] && st[vis[i]]==st[v]+1){
			int k=dfs(vis[i],min(rest,c[i]));
			if(!k) st[vis[i]]=-1;
			c[i]-=k;c[i^1]+=k;rest-=k;
			if(!flow) break;
		}
	return flow-rest;
}
int pt[N],yy[N],zz[N];
int cnt,n,m,x,y,z,aa,bb,cc;
int cur,mn,mnx,mx;
struct point{int x,y,z;}b[5010];
inline int dinic(int mxflow){
	int flow=0;
	while(bfs()){
    	for(int i=0;i<=t;i++) curr[i]=head[i];
		while(flow=dfs(s,INF)){
			mxflow+=flow;
			if(mxflow>=mn) return mxflow;
		}
	}
	return mxflow;
}
inline void solve(int x){
	cnt=s=mx=0;
	int ans=0;
	for(int i=0;i<aa;i++){
		if(x&(1<<i)) ans++,pt[i+1]=0;
		else pt[i+1]=1;
	}
	for(int i=1;i<=cur;i++) if(pt[b[i].x]) yy[++cnt]=b[i].y,zz[cnt]=b[i].z;
	for(int i=1;i<=cnt;i++) mx=max(mx,max(yy[i],zz[i]));
	t=mx*2+1;
	tot=1;for(int i=s;i<=t;i++) head[i]=0;
	for(int i=1;i<=mx;i++) add(s,i,1),add(i+mx,t,1);
	for(int i=1;i<=cnt;i++) add(yy[i],zz[i]+mx,1);
	mn=min(mn,dinic(ans));
}
int bin[50];
int main(){
	int T=read();bin[0]=1;
	for(int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
	while(T--){
		aa=read(),bb=read(),cc=read(),mnx=min(aa,min(bb,cc)),cur=0,mn=INF;
		if(bb==mnx) swap(aa,bb);
		else if(cc==mnx) swap(aa,cc);
		for(int i=1;i<=aa;i++)
			for(int j=1;j<=bb;j++)
				for(int k=1;k<=cc;k++){
					x=read();
					if(x) b[++cur].x=i,b[cur].y=j,b[cur].z=k;
				}
		for(int i=0;i<bin[aa];i++) solve(i);
		cout<<mn<<"\n";
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值