[Bzoj 3438] 小M的作物

3438: 小M的作物

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 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;
}

 

posted @ 2018-04-16 21:59  YoungNeal  阅读(707)  评论(0编辑  收藏  举报