您的位置:首页 > 理论基础 > 计算机网络

网络流:洛谷P1361 小M的作物

2018-02-21 22:16 375 查看

题目描述

小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个整数,表示该组合中的作物编号。
输出格式:只有一行,包括一个整数,表示最大收益

输入输出样例

输入样例:
3
4 2 1
2 3 2
1
2 3 2 1 2

输出样例:
11

说明

样例解释A耕地种1,2,B耕地种3,收益4+2+3+2=11。数据范围1<=k< n<= 1000,0 < m < = 1000 保证所有数据及结果不超过2*10^9。
题目链接:https://www.luogu.org/problemnew/show/P1361
———————————————————————————————————————————————
可以看出是一个最小割,每种作物要么种在A田要么种在B田
建边:
源点当作A田,汇点当作B田;
源点向种子连Ai的边,种子向汇点连Bi的边;
组合x拆成两个点x1、x2;
源点向x1连c1i的边,x1向种子连INF,种子向x2连INF,x2向汇点连c2i;
跑一遍最小割,总收益减去最小割就是最大收益;
原因(转载自elijahqi的CSDN博客):
S向每个点建边,容量为种在A的收益,每个点向T建边,容量为种在B的收益,对于每一个点,我们必然要割掉连向S或T的一条且仅一条边。对于割之后的图,S集的点均选择了种在A,T集的点均选择了种在B。那对于共同种在A地有额外收益的点怎么办呢?,我们新建一个点x,S向x连边,容量为收益,x向所有需要共同种在A的点连边,容量为inf,则如果这些点有一个点没有割掉向T的连边,我们势必要割掉收益这条边。如果收益这条边没被割掉,则说明他所需要的点最后都割了向T的连边,也就是都在S集中,也就是都种在了A,符合题意。对于共同种在B的有额外收益的也同理,新建一个点x,x向T连边,容量为收益,所有需要共同种在B的点向x连边,容量为inf。则最后答案就是总收益-最小割。

AC代码:#include<bits/stdc++.h>
#define o edge[i].t
#define INF INT_MAX
using namespace std;
int cnt=1,n,m,S,T=666666,c1,c2,sum;
int head[1111111],dep[1111111];
struct hh{int t,w,nxt;}edge[1111111<<1];
void make_edge(int f,int t,int w)
{
edge[++cnt]=(hh){t,w,head[f]};
head[f]=cnt;
edge[++cnt]=(hh){f,0,head[t]};
head[t]=cnt;
}
int dfs(int x,int sum)
{
if (x==T) return sum;
int ret=0;
for (int i=head[x];i;i=edge[i].nxt)
{
if (edge[i].w&&dep[o]==dep[x]+1)
{
int k=dfs(o,min(sum,edge[i].w));
edge[i].w-=k;
edge[i^1].w+=k;
sum-=k;
ret+=k;
if (!sum) break;
}
}
if (!ret) dep[x]=0;
return ret;
}
bool bfs()
{
memset(dep,0,sizeof(dep));
queue<int>q;
q.push(S);
dep[S]=1;
while (!q.empty())
{
int t=q.front();
q.pop();
for (int i=head[t];i;i=edge[i].nxt)
if (!dep[o]&&edge[i].w)
{dep[o]=dep[t]+1;q.push(o);}
}
return dep[T];
}
int dinic()
{
while (bfs()) sum-=dfs(S,INF);
return sum;
}
int main()
{
scanf("%d",&n);
int i,j,x,y,c1,c2;
for (i=1;i<=n;i++) scanf("%d",&x),make_edge(S,i,x),sum+=x;
for (i=1;i<=n;i++) scanf("%d",&x),make_edge(i,T,x),sum+=x;
int cur=n;
scanf("%d",&m);
for (i=1;i<=m;i++)
{
sc
9c89
anf("%d %d %d",&x,&c1,&c2);
for (j=1;j<=x;j++)
{
scanf("%d",&y);
make_edge(cur+1,y,INF);
make_edge(y,cur+2,INF);
}
make_edge(S,cur+1,c1);
make_edge(cur+2,T,c2);
sum+=c1+c2;
cur+=2;
}
printf("%d",dinic());
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络流