[Bzoj 3438] 小M的作物
3438: 小M的作物
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1780 Solved: 753
[Submit][Status][Discuss]
Description
小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子
有1个(就是可以种一棵作物)(用1...n编号),现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植
可以获得bi的收益,而且,现在还有这么一种神奇的现象,就是某些作物共同种在一块耕地中可以获得额外的收益
,小M找到了规则中共有m种作物组合,第i个组合中的作物共同种在A中可以获得c1i的额外收益,共同总在B中可以
获得c2i的额外收益,所以,小M很快的算出了种植的最大收益,但是他想要考考你,你能回答他这个问题么?
Input
第一行包括一个整数n
第二行包括n个整数,表示ai第三行包括n个整数,表示bi第四行包括一个整数m接下来m行,
对于接下来的第i行:第一个整数ki,表示第i个作物组合中共有ki种作物,
接下来两个整数c1i,c2i,接下来ki个整数,表示该组合中的作物编号。输出格式
Output
只有一行,包括一个整数,表示最大收益
Range
1<= k< n <= 1000,0 < m < = 1000 保证所有数据及结果不超过2*10^9。
Solution
好题~
最开始想的费用流结果会多求很多答案,看了题解才知道跟最小割有关
我们把源点当做 A 田地,汇点当做 B 田地。
对于作物 i,如果种在 A 的价值是 a[i],种在 B 的价值是 b[i],那么就从它向 A 连一条容量为 a[i] 的边,同理,向 B 连一条容量为 b[i] 的边。
为什么要这么做呢?考虑最后的答案,一个点向外连出的两条边必定会有一条被割掉,不然这个点连着两边,相当于两边都种,不符合题意,所以这种情况不符合题意。
必定有一条会被割掉...咦这不是最小割么?
那么我们已经初步转化问题了,即已经把问题转化到最小割上了。
接下来考虑组合的问题。
其实组合也一样,就是让这个组合也分成向 A,B 连边两部分。
先把这个组合拆点。然后其中一个点的入边连上 A,容量是组合种在 A 的额外价值,出边连上这个组合里所有的点,容量是 INF。 B 也是同理。
那么我们就彻底把问题转化了最小割了。但是这题要让 ans 最大,怎么办呢?
好说,把总收益加起来,减去最小割就好了。
Code
// By YoungNeal #include<queue> #include<cstdio> #include<cstring> #include<iostream> #define N 10005 #define inf 0x3f3f3f3f-1 int tot; int d[N]; int cnt=1; int dis[N]; int head[N]; int n,m,s,t; struct Edge{ int to,nxt,flow; }edge[4400000]; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].flow=z; head[x]=cnt; } bool bfs(){ memset(d,0,sizeof d); d[s]=1; std::queue<int> q; q.push(s); while(q.size()){ int u=q.front();q.pop(); for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(!edge[i].flow) continue; if(d[to]) continue; d[to]=d[u]+1; q.push(to); if(to==t) return 1; } } return 0; } int dinic(int now,int flow){ if(now==t) return flow; int rest=flow; for(int i=head[now];i;i=edge[i].nxt){ if(!rest) return flow; int to=edge[i].to; if(!edge[i].flow) continue; if(d[to]!=d[now]+1) continue; int k=dinic(to,std::min(rest,edge[i].flow)); if(!k) d[to]=0; rest-=k; edge[i].flow-=k; edge[i^1].flow+=k; } return flow-rest; } signed main(){ scanf("%d",&n); s=n+1; t=s+1; for(int x,i=1;i<=n;i++) scanf("%d",&x),tot+=x,add(s,i,x),add(i,s,0); for(int x,i=1;i<=n;i++) scanf("%d",&x),tot+=x,add(i,t,x),add(t,i,0); scanf("%d",&m); for(int i=1;i<=m;i++){ int T,tota,totb; scanf("%d%d%d",&T,&tota,&totb); tot+=tota+totb; add(s,n+2+i,tota); add(n+2+i,s,0); add(n+2+i+m,t,totb); add(t,n+2+i+m,0); for(int x,j=1;j<=T;j++){ scanf("%d",&x); add(n+2+i,x,inf); add(x,n+2+i,0); add(x,n+2+i+m,inf); add(n+2+i+m,x,0); } } int maxflow=0,flow=0; while(bfs()) while(flow=dinic(s,0x3f3f3f3f)) maxflow+=flow; printf("%d\n",tot-maxflow); return 0; }
当你走进这欢乐场