#1752. 小M 的作物(crop)

题目描述
小M 还是个特么喜欢玩MC 的孩纸。。。

小M 在MC 里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有nn中作物的种子,每种作物的种子有11个 ( 就是可以种一棵作物) (用1 …n 1…n 编号),现在,第 ii 种作物种植在A中种植可以获得 a_ia
i

的收益,在B 中种植可以获得 b_ib
i

的收益,而且,现在还有这么一种神奇的现象,就是某些作物共同种在一块耕地中可以获得额外的收益,小M 找到了规则中共有 mm 种作物组合,第 ii 个组合中的作物共同种在A 中可以获得c1_ic1
i

的额外收益,共同总在B 中可以获得c2_ic2
i

的额外收益,所以,小M很快的算出了种植的最大收益,但是他想要考考你,你能回答他这个问题么?
输入格式
第一行包括一个整数 nn
第二行包括nn 个整数,表示a_ia
i

第三行包括nn 个整数,表示b_ib
i

第四行包括一个整数mm
接下来mm 行,对于接下来的第ii 行:第一个整数k_ik
i

,表示第ii个作物组合中共有k_ik
i

种作物,
接下来两个整数c1_ic1
i

,c2_ic2
i

,接下来k_ik
i

个整数,表示该组合中的作物编号。
输出格式
只有一行,包括一个整数,表示最大收益
样例
样例输入

3
4 2 1
2 3 2
1
2 3 2 1 2
样例输出

11
数据范围与提示
【样例解释】
A 耕地种1,2,B 耕地种3,收益4+2+3+2=11。

【数据范围与约定】
对于20%的数据,1 \le k < n , 0 < m \le 10 1≤k<n,0<m≤10
对于100%的数据, 1 \le k < n \le 1000 , 0 < m \le 1000 1≤k<n≤1000,0<m≤1000
保证所有数据及结果不超过 2 * 10 ^9 2∗10
9

来源
Kpmcup#0
最小割模板题:
二选一时,最小割。
同时集合内任意一个点选对面,必须舍弃集合,不然不满足割。
注意:
在一个图中,割去权值和最小的边集,使这个图分成
两个部分,切下来的那一刀叫做最小割。最小割并不唯一,但它在数值上是等于最大流的。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstdio>
#define N 10005
using namespace std;
const int inf=1<<29;
int head[N],nex[4400000],ver[4400000],edge[4400000];
int tot=1,d[N],s,n,m,t;
inline void add(int x,int y,int z){
	nex[++tot]=head[x];head[x]=tot;ver[tot]=y;edge[tot]=z;
}
queue<int> q;
bool bfs(){
	memset(d,0,sizeof(d));
	while(q.size())q.pop();
	q.push(s);d[s]=1;
	while(q.size()){
		int x=q.front();q.pop();
		for(int i=head[x];i;i=nex[i]){
			int y=ver[i];
			if(!edge[i]||d[y])continue;
			d[y]=d[x]+1;q.push(y);
			if(y==t)return 1;
		}
	}
	return 0;
}
int dinic(int x,int flow){
	if(x==t)return flow;
	int rest=flow,k;
	for(int i=head[x];i&&rest;i=nex[i]){
		int y=ver[i];
		if(!edge[i]||d[y]!=d[x]+1)continue;
	    k=dinic(y,min(rest,edge[i]));
	    if(!k)d[y]=0;
	    rest-=k;edge[i]-=k;edge[i^1]+=k;
	}
	return flow-rest;
}
int a[N],b[N];
int main(){
	scanf("%d",&n);int sum=0;
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	for(int i=1;i<=n;++i)scanf("%d",&b[i]);
	cin>>m;s=n+2*m+1;t=n+2*m+2;
	for(int i=1;i<=n;++i){
		sum+=a[i]+b[i];
		add(s,i,a[i]);add(i,s,0);
		add(i,t,b[i]);add(t,i,0);
	}
	for(int i=1;i<=m;++i){
		int c1,c2,k;scanf("%d%d%d",&k,&c1,&c2);
		int l=i+n,r=i+n+m;sum+=c1+c2;
		add(s,l,c1);add(l,s,0);add(r,t,c2);add(t,r,0);
		for(int j=1;j<=k;++j){
			int x;scanf("%d",&x);
			add(l,x,inf);add(x,l,0);add(x,r,inf);add(r,x,0);
		}
	}
//	cout<<sum<<endl;
	int maxflow=0,flow=0;
	while(bfs())while(flow=dinic(s,inf))maxflow+=flow;
//	cout<<maxflow<<endl;
	cout<<sum-maxflow<<endl;
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值