【bzoj3130】[Sdoi2013]费用流 二分+网络流最大流
题目描述
Alice和Bob做游戏,给出一张有向图表示运输网络,Alice先给Bob一种最大流方案,然后Bob在所有边上分配总和等于P的非负费用。Alice希望总费用尽量小,而Bob希望总费用尽量大。求两人都采取最优策略的情况下最大流及总费用。
输入
第一行三个整数N,M,P。N表示给定运输网络中节点的数量,M表示有向边的数量,P的含义见问题描述部分。为了简化问题,我们假设源点S是点1,汇点T是点N。
接下来M行,每行三个整数A,B,C,表示有一条从点A到点B的有向边,其最大流量是C。
输出
第一行一个整数,表示最大流的值。
第二行一个实数,表示总费用。建议选手输出四位以上小数。
样例输入
3 2 1
1 2 10
2 3 15
样例输出
10
10.000
题解
二分+网络流最大流
显然对于Alice给出的一种方案,Bob只需要在流量最大的边上设置费用P,其它边费用为0,即可使费用最大。
所以Alice要使费用尽量小,就需要使流量最大的边的流量最小。
先跑一遍最大流得出第一问的答案。然后二分最大流量,对于一条边,将其容量设置为 min(原图中流量,mid) ,跑最大流,如果最大流等于原图的最大流则可行,否则不可行。
最后的答案就是mid*P。
我才不会告诉你们第二问puts("nan")可过呢
#include <queue> #include <cstdio> #include <cstring> #define N 110 #define M 2010 using namespace std; queue<int> q; int m , px[M] , py[M] , head[N] , to[M] , next[M] , cnt , s , t , dis[N]; double pz[M] , val[M]; inline void add(int x , int y , double 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; } double dinic(int x , double low) { if(x == t) return low; double temp = low , k; int i; 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; } double solve(double mid) { int i; double ans = 0; memset(head , 0 , sizeof(head)) , cnt = 1; for(i = 1 ; i <= m ; i ++ ) add(px[i] , py[i] , min(pz[i] , mid)); while(bfs()) ans += dinic(s , 1e9); return ans; } int main() { int n , i , cnt = 50; double l = 0 , r = 1e9 , mid , flow , p; scanf("%d%d%lf" , &n , &m , &p) , s = 1 , t = n; for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%lf" , &px[i] , &py[i] , &pz[i]); printf("%.0lf\n" , flow = solve(1e9)); while(cnt -- ) { mid = (l + r) / 2; if(solve(mid) == flow) r = mid; else l = mid; } printf("%.4lf\n" , r * p); return 0; }