zoukankan      html  css  js  c++  java
  • 【bzoj3438】小M的作物 网络流最小割

    原文地址:http://www.cnblogs.com/GXZlegend/p/6801522.html


    题目描述

    小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


    题解

    网络流最小割

    最大收益=总收益-最小损失

    最小损失可以通过最小割来求。

    设与S相连表示种在A,与T相连表示种在B。

    每个作物不能同时种在A和B,应选择一个割掉,故连边S->i,容量为ai;i->T,容量为bi。

    对于每个组合,如果这个组合都种在A,那么任意一个都不能种在B,应割掉,故连边S->kai,容量为c1i;kai->p,容量为inf。

    都种在B同理。

    然后求最小割,用总收益减去最小割即为答案。

    数组好像要开很大才能过。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define inf 0x7fffffff
    using namespace std;
    queue<int> q;
    int head[3050] , to[4100000] , val[4100000] , next[4100000] , cnt = 1 , s , t , dis[3050];
    void add(int x , int y , int z)
    {
    	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    	to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
    }
    bool bfs()
    {
    	int x , i;
    	while(!q.empty()) q.pop();
    	memset(dis , 0 , sizeof(dis));
    	dis[s] = 1 , q.push(s);
    	while(!q.empty())
    	{
    		x = q.front() , q.pop();
    		for(i = head[x] ; i ; i = next[i])
    		{
    			if(val[i] && !dis[to[i]])
    			{
    				dis[to[i]] = dis[x] + 1;
    				if(to[i] == t) return 1;
    				q.push(to[i]);
    			}
    		}
    	}
    	return 0;
    }
    int dinic(int x , int low)
    {
    	if(x == t) return low;
    	int temp = low , i , k;
    	for(i = head[x] ; i ; i = next[i])
    	{
    		if(val[i] && dis[to[i]] == dis[x] + 1)
    		{
    			k = dinic(to[i] , min(temp , val[i]));
    			if(!k) dis[to[i]] = 0;
    			val[i] -= k , val[i ^ 1] += k;
    			if(!(temp -= k)) break;
    		}
    	}
    	return low - temp;
    }
    int main()
    {
    	int n , m , i , k , a , b , x , tot , sum = 0;
    	scanf("%d" , &n);
    	s = 0 , t = tot = n + 1;
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a) , add(s , i , a) , sum += a;
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &b) , add(i , t , b) , sum += b;
    	scanf("%d" , &m);
    	while(m -- )
    	{
    		scanf("%d%d%d" , &k , &a , &b);
    		add(s , ++tot , a) , add(++tot , t , b) , sum += a + b;
    		while(k -- ) scanf("%d" , &x) , add(tot - 1 , x , inf) , add(x , tot , inf);
    	}
    	while(bfs()) sum -= dinic(s , inf);
    	printf("%d
    " , sum);
    	return 0;
    }

     

  • 相关阅读:
    逝华
    数论知识
    #10081. 「一本通 3.2 练习 7」道路和航线 题解
    Tire 字典树
    Manacher算法
    时间变奏曲
    【算法】莫队
    【算法】深度优先搜索(dfs)
    【算法】数位 dp
    【笔记】关于位运算(2)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6801522.html
Copyright © 2011-2022 走看看