洛谷 P1361 小M的作物
题目描述
小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子有1个(就是可以种一棵作物)(用1...n编号)。
现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植可以获得bi的收益,而且,现在还有这么一种神奇的现象,就是某些作物共同种在一块耕地中可以获得额外的收益,小M找到了规则中共有m种作物组合,第i个组合中的作物共同种在A中可以获得c1i的额外收益,共同总在B中可以获得c2i的额外收益。
小M很快的算出了种植的最大收益,但是他想要考考你,你能回答他这个问题么?
输入输出格式
输入格式:
第一行包括一个整数n
第二行包括n个整数,表示ai第三行包括n个整数,表示bi第四行包括一个整数m接下来m行,
对于接下来的第i行:第一个整数ki,表示第i个作物组合中共有ki种作物,
接下来两个整数c1i,c2i,接下来ki个整数,表示该组合中的作物编号。
输出格式:
只有一行,包括一个整数,表示最大收益
输入输出样例
输入样例#1: 复制
3
4 2 1
2 3 2
1
2 3 2 1 2
输出样例#1: 复制
11
说明
样例解释
A耕地种1,2,B耕地种3,收益4+2+3+2=11。
数据范围与约定
1<=k< n<= 1000,0 < m < = 1000 保证所有数据及结果不超过2*10^9。
思路
先考虑没有额外收益情况下的建图。
设源点S属于集合A,汇点T属于集合B,则显然点与源点所连的有向边为这个点归属于集合A所产生的收益,与汇点相连则同理。则原图边权之和-最小割就是本题的答案了。
在这个图的基础上,考虑如何把点集给加入。
首先明确一点,点集的贡献有三种情况,对集合A贡献,对集合B贡献或者不贡献。这意味着只划分出一种状态是无法描述的,至少要把A与B的情况分开描述。
讨论一个对 A 有贡献的点集{c,d} 。
依据题目,我们对这个点集的要求是,只要 c,d 有一个点被割到了集合 T ,这个点集都无法产生贡献。换而言之,只要 c 或 d在集合 B ,代表点集贡献的边必须要断开。
先尝试着连接这条贡献边,因为这条边不可能直接连到图中代表作物的点上,所以连接一个虚点上去。
X为点集所产生的虚点。
如果 c 被割到了集合 B ,则所有从 S 到 c 的路径都得被断开(具体只路径的一条边断掉),如果我们想要s->x断掉,那么虚点 X 连 c 的那条路径不能被断掉。而容量为正无穷的边不可能被断掉,于是我们这样建图。
两条x->d,x->c边的边权为 inf
对待贡献 B 集合的点,同理
建图跑网络流即可。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3fffffff
using namespace std;
const int maxn=10010, maxm=2e6+7;
int first[maxn],sign,cur[maxn];
int s,t,d[maxn];
int mp[maxn][maxn];
struct node{
int to,w,next;
}edge[maxm];
void init(){
memset(first,-1,sizeof(first));
sign=0;
}
void add_edge(int u,int v,int w){
edge[sign].to=v;
edge[sign].w=w;
edge[sign].next=first[u];
first[u]=sign++;
edge[sign].to=u;
edge[sign].w=0;
edge[sign].next=first[v];
first[v]=sign++;
}
int bfs(){
queue<int>q;
memset(d,0,sizeof(d));
d[s]=1;
q.push(s);
while(!q.empty()){
int top=q.front();
q.pop();
for(int i=first[top];~i;i=edge[i].next){
int to=edge[i].to;
if(edge[i].w>0&&d[to]==0){
d[to]=d[top]+1;
if(to==t)
return 1;
q.push(to);
}
}
}
return d[t]!=0;
}
int dfs(int top,int flow){
if(top==t)
return flow;
int ans=0,x=0;
for(int i=cur[top];~i;i=edge[i].next){
int to=edge[i].to;
if(edge[i].w>0&&d[to]==d[top]+1){
x=dfs(to,min(flow-ans,edge[i].w));
edge[i].w-=x;
edge[i^1].w+=x;
if(edge[i].w)
cur[top] = i;
ans+=x;
if(ans==flow)
return flow;
}
}
if(ans==0)
d[top]=0;
return ans;
}
int dinic(int n){
int ans=0;
while(bfs()){
for(int i=0;i<=n;i++)
cur[i]=first[i];
ans+=dfs(s,inf);
}
return ans;
}
int main(){
int n,w,a,b,u,k,m;
init();
scanf("%d",&n);
s = 0;
t = 10009;
int sum = 0;
for(int i = 1;i<= n;i ++)
{
scanf("%d",&w);
sum += w;
add_edge(s,i,w);
}
for(int i = 1;i <= n;i ++)
{
scanf("%d",&w);
sum += w;
add_edge(i,t,w);
}
scanf("%d",&m);
for(int i = n + 1;i <= n + m;i ++)
{
scanf("%d %d %d",&k,&a,&b);
sum = sum + a + b;
add_edge(s,i,a);
add_edge(i + m,t,b);
for(int j = 0;j < k;j ++)
{
scanf("%d",&u);
add_edge(i,u,inf);
add_edge(u,i+m,inf);
}
}
int ans = dinic(t);
printf("%d",sum - ans);
return 0;
}