bzoj4177Mike的农场 网络流最小割
Posted GXZlegend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4177Mike的农场 网络流最小割相关的知识,希望对你有一定的参考价值。
题目描述
Mike有一个农场,这个农场n个牲畜围栏,现在他想在每个牲畜围栏中养一只动物,每只动物可以是牛或羊,并且每个牲畜围栏中的饲养条件都不同,其中第i个牲畜围栏中的动物长大后,每只牛可以卖a[i]元,每只羊可以卖b[i]元,为了防止牛羊之间相互影响,Mike找到了m条规律,每条规律给出一个三元组(i, j, k)表示如果第i个围栏和第j个围栏养的是不同的动物,那么Mike就需要花费k的代价请人帮忙处理牛羊之间的影响。不过同时Mike也发现k条特殊的规则(S, a, b),表示如果S中所有牲畜围栏中都养的是动物a,那么Mike可以获得b的额外收入。现在Mike想知道他该在哪些围栏中饲养什么动物才能使得总收益最大,为了简化问题,你只需要输出最大收益。
输入
第一行三个整数n、m、k,表示一共有n个围栏,m条规律,k条规则。
第二行有n个整数,表示a[i]。
第三行有n个整数,表示b[i]。
接下来m行,每行有三个整数(i, j, k)表示一条规则。
再接下来k行,每行一开始有三个整数t、a和b,表示一条规则(S, a, b),其中S的大小为t,接下来t个整数表示S中的元素(a为0表示全为牛,a为1表示全为羊)。
输出
输出一个整数ans,表示最大收益。
样例输入
4 2 1
1 2 3 1
2 3 1 2
1 2 3
1 3 2
2 0 100 1 2
样例输出
108
题解
经典的网络流最小割建模
建模方法:
S向x连边,容量为a[x];x向T连边,容量为b[x];
如果两个选择不同则要付出代价,那么在它们之间连双向边,容量为代价k;
如果P集合内全部选择S端则获得额外收益,那么建一个新点p代表集合,S向p连边,容量为收益,p向P中每个点连边,容量为$\infty$;全部选择T端同理,每个点连p,p连T。
跑最小割,答案为所有收益之和-最小割。
#include <queue> #include <cstdio> #include <cstring> #define N 10010 #define M 1000010 #define inf 1 << 30 using namespace std; queue<int> q; int head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N]; inline 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; memset(dis , 0 , sizeof(dis)); while(!q.empty()) q.pop(); 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 , k , i , x , y , z , sum = 0; scanf("%d%d%d" , &n , &m , &k) , s = 0 , t = n + k + 1; for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &x) , add(s , i , x) , sum += x; for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &x) , add(i , t , x) , sum += x; for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d" , &x , &y , &z) , add(x , y , z) , add(y , x , z); for(i = 1 ; i <= k ; i ++ ) { scanf("%d%d%d" , &x , &y , &z) , sum += z; if(y) { add(i + n , t , z); while(x -- ) scanf("%d" , &y) , add(y , i + n , inf); } else { add(s , i + n , z); while(x -- ) scanf("%d" , &y) , add(i + n , y , inf); } } while(bfs()) sum -= dinic(s , inf); printf("%d\n" , sum); return 0; }
以上是关于bzoj4177Mike的农场 网络流最小割的主要内容,如果未能解决你的问题,请参考以下文章