bzoj4177: Mike的农场

题面: Mike有一个农场,这个农场n个牲畜围栏,现在他想在每个牲畜围栏中养一只动物,每只动物可以是牛或羊,并且每个牲畜围栏中的饲养条件都不同,其中第i个牲畜围栏中的动物长大后,每只牛可以卖a[i]元,每只羊可以卖b[i]元,为了防止牛羊之间相互影响,Mike找到了m条规律,每条规律给出一个三元组(i, j, k)表示如果第i个围栏和第j个围栏养的是不同的动物,那么Mike就需要花费k的代价请人帮忙处理牛羊之间的影响。不过同时Mike也发现k条特殊的规则(S, a, b),表示如果S中所有牲畜围栏中都养的是动物a,那么Mike可以获得b的额外收入。现在Mike想知道他该在哪些围栏中饲养什么动物才能使得总收益最大,为了简化问题,你只需要输出最大收益。

思路:最小割。

先把所有的收益加起来,然后建图跑最小割,最小割就是最小损失的收益,总收益-最小损失就是答案

对于每个围栏建一个点,由S向每个点连a[i]的边,由每个点再向T连b[i]的边,最终与S相连,那么它就割掉了连向T的边,表示最终这个围栏养牛,将损失养羊的收益a[i],与T相连同理。

对于规律(i,j,k),就在(i,j)之间连权值为k的双向边,其实就是组合收益的建图

对于规则(S,a,b),就新建一个点,并向S中所有点连inf的边,如果是牛就从源向新点连一条权值为b的边,否则向汇连。

以都选牛有收益为例,当两个点都选了牛,就会割掉羊的边,那么这条新边就不会被割了,我们就得到了b的收益。


代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=20010,maxm=1000010,maxq=1000000,inf=(int)1e9;
using namespace std;
int n,m,k,a[maxn],b[maxn],dis[maxn],pre[maxm],now[maxn],son[maxm],val[maxm],tot=1,S=0,T=maxn-1,q[maxq+10],head,tail,ans,res;char ch;
void add(int a,int b,int c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;}
void ins(int a,int b,int c){add(a,b,c),add(b,a,0);}

void read(int &x){
	for (ch=getchar();!isdigit(ch);ch=getchar());
	for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
}

bool bfs(){
	memset(dis,-1,sizeof(dis));
	q[1]=S,head=0,tail=1,dis[S]=0;
	do{
		if (++head>maxq) head=1;
		int x=q[head];
		for (int y=now[x];y;y=pre[y])
			if (dis[son[y]]==-1&&val[y]){
				if (++tail>maxq) tail=1;
				dis[son[y]]=dis[x]+1,q[tail]=son[y];
			}
	}while (head!=tail);
	return dis[T]>0;
}

int find(int x,int low){
	int res=0,y;
	if (x==T) return low;
	for (y=now[x];y;y=pre[y]){
		if (dis[son[y]]!=dis[x]+1||!val[y]) continue;
		int tmp=find(son[y],min(low,val[y]));
		val[y]-=tmp,val[y^1]+=tmp,res+=tmp,low-=tmp;
		if (!low) break;
	}
	if (!y) dis[x]=-1;
	return res;
}

int main(){
	scanf("%d%d%d",&n,&m,&k);
	for (int i=1;i<=n;i++) read(a[i]),ins(S,i,a[i]),ans+=a[i];
	for (int i=1;i<=n;i++) read(b[i]),ins(i,T,b[i]),ans+=b[i];
	for (int i=1,x,y,z;i<=m;i++) read(x),read(y),read(z),add(x,y,z),add(y,x,z);
	for (int i=1,t,x,y;i<=k;i++){
		read(t),read(x),read(y),ans+=y;
		if (!x){
			ins(S,++n,y);
			for (int j=1,z;j<=t;j++) read(z),ins(n,z,inf);
		}
		else{
			ins(++n,T,y);
			for (int j=1,z;j<=t;j++) read(z),ins(z,n,inf);
		}
	}
	while (bfs()) res+=find(S,inf);
	printf("%d\n",ans-res);
	return 0;
}


posted @ 2015-07-14 11:34  orzpps  阅读(199)  评论(0编辑  收藏  举报