zoukankan      html  css  js  c++  java
  • [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
    ",tot-maxflow);
        return 0;
    }
  • 相关阅读:
    Linux w命令
    01.drf文档及外键字段反序列化
    redis的参数解释
    redis集群复制和故障转移
    codis原理及部署_01
    redis 主从哨兵02
    redis 主从哨兵01
    redis持久化
    redis python操作
    redis cluster
  • 原文地址:https://www.cnblogs.com/YoungNeal/p/8858838.html
Copyright © 2011-2022 走看看