BZOJ3130 [Sdoi2013]費用流 【網絡流 + 二分】
題目
Alice和Bob在圖論課程上學習了最大流和最小費用最大流的相關知識。
最大流問題:給定一張有向圖表示運輸網絡,一個源點S和一個匯點T,每條邊都有最大流量。一個合法的網絡流方案必須滿足:(1)每條邊的實際流量都不超過其最大流量且非負;(2)除了源點S和匯點T之外,對於其余所有點,都滿足該點總流入流量等於該點總流出流量;而S點的凈流出流量等於T點的凈流入流量,這個值也即該網絡流方案的總運輸量。最大流問題就是對於給定的運輸網絡,求總運輸量最大的網絡流方案。
上圖表示了一個最大流問題。對於每條邊,右邊的數代表該邊的最大流量,左邊的數代表在最優解中,該邊的實際流量。需要註意到,一個最大流問題的解可能不是唯一的。 對於一張給定的運輸網絡,Alice先確定一個最大流,如果有多種解,Alice可以任選一種;之後Bob在每條邊上分配單位花費(單位花費必須是非負實數),要求所有邊的單位花費之和等於P。總費用等於每一條邊的實際流量乘以該邊的單位花費。需要註意到,Bob在分配單位花費之前,已經知道Alice所給出的最大流方案。現茌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.0000
提示
【樣例說明】
對於Alice,最大流的方案是固定的。兩條邊的實際流量都為10。
對於Bob,給第一條邊分配0.5的費用,第二條邊分配0.5的費用。總費用
為:100.5+100.5=10。可以證明不存在總費用更大的分配方案。
【數據規模和約定】
對於20%的測試數據:所有有向邊的最大流量都是1。
對於100%的測試數據:N < = 100,M < = 1000。
對於l00%的測試數據:所有點的編號在I..N範圍內。1 < = 每條邊的最大流
量 < = 50000。1 < = P < = 10。給定運輸網絡中不會有起點和終點相同的邊。
題解
首先,Bob的最優策略一定是將所有權值加到流量最大的邊上
所以Alice應使在最大流前提下使最大流量的邊最小
那麽二分所有邊的上限判定可行性就可以了
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
#define eps 1e-8
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
using namespace std;
const int maxn = 105,maxm = 5005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
int n,m,h[maxn],ne = 2,S,T;
double P,M;
struct EDGE{int to,nxt; double f,cap;}ed[maxm];
inline void build(int u,int v,double w){
ed[ne] = (EDGE){v,h[u],w,w}; h[u] = ne++;
ed[ne] = (EDGE){u,h[v],0,0}; h[v] = ne++;
}
int d[maxn],vis[maxn],cur[maxn];
queue<int> q;
bool bfs(){
for (int i = 1; i <= n; i++) d[i] = INF,vis[i] = false;
q.push(S); d[S] = 0; vis[S] = true;
int u;
while (!q.empty()){
u = q.front(); q.pop();
Redge(u) if (fabs(ed[k].f) > eps && !vis[to = ed[k].to]){
d[to] = d[u] + 1; vis[to] = true;
q.push(to);
}
}
return vis[T];
}
double dfs(int u,double minf){
if (u == T || fabs(minf) < eps) return minf;
double flow,f; int to;
if (cur[u] == -1) cur[u] = h[u];
for (int& k = cur[u]; k; k = ed[k].nxt)
if (d[to = ed[k].to] == d[u] + 1 && fabs(f = dfs(to,min(minf,ed[k].f))) > eps){
ed[k].f -= f; ed[k ^ 1].f += f;
flow += f; minf -= f;
if (fabs(minf) < eps) break;
}
return flow;
}
double maxflow(){
double flow = 0;
while (bfs()){
memset(cur,-1,sizeof(cur));
flow += dfs(S,INF);
}
return flow;
}
bool check(double cap){
for (int i = 2; i < ne; i += 2){
ed[i ^ 1].f = 0;
ed[i].f = min(ed[i].cap,cap);
}
return maxflow() >= M;
}
void solve(){
M = maxflow();
double l = 0,r = 50000,mid;
while ((r - l) > 1e-4){
mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
printf("%.lf\n%.4lf\n",M,l * P);
}
int main(){
n = read(); m = read(); P = read(); S = 1; T = n;
int a,b,w;
for (int i = 1; i <= m; i++){
a = read(); b = read(); w = read();
build(a,b,w);
}
solve();
return 0;
}
BZOJ3130 [Sdoi2013]費用流 【網絡流 + 二分】