• 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
    ",ans-res);
    	return 0;
    }


  • 相关阅读:
    价值观
    周记 改
    周记
    C语言问卷调查
    icon踩坑记录
    console.log(a)和console.log(window.a)的区别?
    记录一次微信二次分享的优化过程
    在jQuery中使用自定义属性
    10个JS技巧
    日常工作总结 2019/10/10
  • 原文地址:https://www.cnblogs.com/thythy/p/5493580.html
Copyright © 2020-2023  润新知