洛谷 P1361 小M的作物 【最小割】

洛谷 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;
}

猜你喜欢

转载自blog.csdn.net/ii0789789789/article/details/81979688