BZOJ4177Mike的农场——最小割

题目描述

 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想知道他该在哪些围栏中饲养什么动物才能使得总收益最大,为了简化问题,你只需要输出最大收益。

输入

第一行三个整数n、m、k,表示一共有n个围栏,m条规律,k条规则。

第二行有n个整数,表示a[i]。

第三行有n个整数,表示b[i]。

接下来m行,每行有三个整数(i, j, k)表示一条规则。

再接下来k行,每行一开始有三个整数t、a和b,表示一条规则(S, a, b),其中S的大小为t,接下来

t个整数表示S中的元素(a为0表示全为牛,a为1表示全为羊)。

输出

输出一个整数ans,表示最大收益。

 

样例输入

4 2 1
1 2 3 1
2 3 1 2
1 2 3
1 3 2
2 0 100 1 2

样例输出

108

提示

 对于100的数据,n <= 5000, m <= 5000, k <= 5000, a = 0 or 1。

 

我们需要对每个围栏进行决策——选牛还是选羊。那么就将源点看成牛,将汇点看成羊,对于每个围栏分别与源汇点连边,流量为对应选牛或羊的代价。对于每个规则将对应点之间连双向边,流量为对应代价。对于特殊规则,如果是选牛的就新建一个节点与源点连边,流量为对应收益,反之则与汇点连边,然后再将这个点与特殊规则指定集合中的点分别连边,流量为$INF$。最后答案为所有收益之和$-$最小割。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define INF 1000000000
using namespace std;
int head[40000];
int next[160000];
int to[160000];
int val[160000];
int d[40000];
int q[40000];
int n,m,k;
int s,x,y,v;
int a[6000];
int b[6000];
int tot=1;
int ans;
int S,T;
void add(int x,int y,int v)
{
	tot++;
	next[tot]=head[x];
	head[x]=tot;
	to[tot]=y;
	val[tot]=v;
	tot++;
	next[tot]=head[y];
	head[y]=tot;
	to[tot]=x;
	val[tot]=0;
}
bool bfs(int S,int T)
{
	int r=0;
	int l=0;
	memset(q,0,sizeof(q));
	memset(d,-1,sizeof(d));
	q[r++]=S;
	d[S]=0;
	while(l<r)
	{  
		int now=q[l];
		for(int i=head[now];i;i=next[i])
		{
			if(d[to[i]]==-1&&val[i]!=0)
			{
				d[to[i]]=d[now]+1;
				q[r++]=to[i];
			}
		}
		l++;
	}
	return d[T]!=-1;
}
int dfs(int x,int flow)
{
	if(x==T)
	{
		return flow;
	}
	int now_flow;
	int used=0;
	for(int i=head[x];i;i=next[i])
	{
		if(d[to[i]]==d[x]+1&&val[i]!=0)
		{
			now_flow=dfs(to[i],min(flow-used,val[i]));
			val[i]-=now_flow;
			val[i^1]+=now_flow;
			used+=now_flow;
			if(now_flow==flow)
			{
				return flow;
			}
		}
	}
	if(used==0)
	{
		d[x]=-1;
	}
	return used;
}
void dinic()
{
	while(bfs(S,T)==true)
	{
		ans-=dfs(S,0x3f3f3f);
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	S=n+k+1;
	T=S+1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		add(S,i,a[i]);
		ans+=a[i];
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&b[i]);
		add(i,T,b[i]);
		ans+=b[i];
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&v);
		add(x,y,v);
		add(y,x,v);
	}
	for(int i=1;i<=k;i++)
	{
		scanf("%d%d%d",&s,&y,&v);
		ans+=v;
		if(!y)
		{
			add(S,n+i,v);
		}
		else
		{
			add(n+i,T,v);
		}
		for(int j=1;j<=s;j++)
		{
			scanf("%d",&x);
			if(!y)
			{
				add(n+i,x,INF);
			}
			else
			{
				add(x,n+i,INF);
			}
		}
	}
	dinic();
	printf("%d",ans);
}

转载于:https://www.cnblogs.com/Khada-Jhin/p/10572027.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值