BZOJ上沒有題幹,這裏直接貼一份:
[Description]
四川的方伯伯爲了致富,決定引進海南的椰子樹。方伯伯的椰子園十分現代化,椰子園中有一套獨特的交通系統。
現在用點來表示交通節點,邊來表示道路。這樣,方伯伯的椰子園就可以看作一個有 n + 2 個交通節點,m條邊的有向無環圖。n +1 號點爲入口,n +2 號點爲出口。每條道路都有 6 個參數,ui,vi,ai,bi,ci,di,分別表示,該道路從 ui 號點通向 vi 號點,將它的容量壓縮一次要 ai 的花費,容量擴大一次要 bi 的花費,該條道路當前的運輸容量上限爲 ci,並且每單位運輸量通過該道路要 di 的費用。
在這個交通網絡中,只有一條道路與起點相連。因爲弄壞了這條道路就會導致整個交通網絡癱瘓,聰明的方伯伯決定絕不對這條道路進行調整,也就是說,現在除了這條道路之外,對其餘道路都可以進行調整。
有兩種調整方式:
1. 選擇一條道路,將其進行一次壓縮,這條道路的容量會下降 1 單位。
2. 選擇一條道路,將其進行一次擴容,這條道路的容量會上升 1 單位。
一條道路可以被多次調整。
由於很久以前,方伯伯就請過一個工程師,對這個交通網絡進行過一次大的優化調整。所以現在所有的道路都被完全的利用起來了,即每條道路的負荷都是滿的(每條道路的流量等於其容量)。
但方伯伯一想到自己的海南椰子會大豐收,就十分擔心巨大的運輸量下,會導致過多的花費。因此,方伯伯決定至少進行一次調整,調整之後,必須要保持每條道路滿負荷,且總交通量不會減少。
設調整後的總費用是 Y,調整之前的總費用是 X。現在方伯伯想知道,最優調整比率是多少,即假設他進行了 k 次調整,(X - Y)/k最大能是多少?
注:總費用 = 交通網絡的運輸花費 + 調整的花費
[Input]
第 1 行包含 2 個整數 n,m
接下來 m 行代表 m 條邊,表示這個交通網絡。
每行 6 個整數,表示 ui,vi,ai,bi,ci,di.
接下來 1 行包含 1 條邊,表示連接起點的邊
[Output]
一個浮點數,保留 2 位小數,表示要求的答案,數據保證答案大於 0
[Sample]
樣例輸入 | 樣例輸出 |
6 7 1 2 0 0 1 1000 2 4 0 0 1 1000 4 6 0 0 1 1000 1 3 0 0 0 0 3 5 0 0 0 0 5 6 0 0 0 0 6 8 0 0 1 0 7 1 0 0 1 0 |
500.00 |
[Hint]
對於 20% 的數據, 1 <= n <= 5, 0 <= m <= 10.
對於 40% 的數據, 1 <= n <= 50, 0 <= m <= 300.
對於 100% 的數據,1 <= n <= 500,0 <= m <= 3000,1 <= ui, vi <= n + 2
0 <= ai, bi <= 50,0 <= ci <= 1000,0 <= di <= 1000
==============這裏纔是題解的開始==============
我們認爲方伯伯是在跑費用流。對於原圖中每一條邊i,建一條正向的,容量正無窮,邊權爲bi + di的邊;如果流量ci不爲0,再建一條反向的,容量爲ci,邊權爲ai - di的邊。這樣我們就構造出了殘量網絡。
這道題由於保證有大於零的解,即費用一定能減少,所以增加流量是一定沒有好下場的(費用一定增加)。那麼我們只能通過調整流量來減少費用。這個時候我們需要——
消圈定理:殘量網絡裏如果存在負費用圈,那麼當前流不是最小費用流。因爲通過增加殘量網絡負權邊的流量,減少正權邊的流量,一定能得到另一個更優的可行流。
由於這道題求的不是最優的費用流,而是最優的調整比率。所以最優解一定是隻調整一個負權環(參見分數規劃相關性質的證明自己YY去)。那麼題目變成,求平均權值最小的負權環。
沒什麼說的了,最基本的分數規劃題。
(本文是爲了打破willinglive在這道題題解上的壟斷而作。willinglive的題解地址:http://blog.csdn.net/willinglive/article/details/42772715)
(話說昨年省選打醬油的時候還真是弱得1B啊,現在看來這麼簡單……)
(不要隨便看代碼,不要隨便看代碼,不要隨便看代碼。因爲很重要說以說三遍)
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
using namespace std;
//Global Variables & Definitions
int N, M;
//End Global Variables & Definitions
//Map
int h[510];
struct edge {
int v, next;
double w;
} e[7000];
int ecnt;
void adde(int u, int v, double w) {
++ecnt;
e[ecnt].v = v;
e[ecnt].w = w;
e[ecnt].next = h[u];
h[u] = ecnt;
}
double dis[510];
int inq[510];
bool spfa(int n) {
inq[n] = 1;
int v;
for(int i = h[n];~i;i = e[i].next) if(dis[v = e[i].v] > dis[n] + e[i].w) {
dis[v] = dis[n] + e[i].w;
if(inq[v]) return true;
if(spfa(v)) return true;
}
inq[n] = 0;
return false;
}
bool TEST() {
for(int i = 0;i < 510;++i) { dis[i] = 0.0; inq[i] = 0; }
for(int i = 1;i <= N;++i) if(spfa(i)) return true;
return false;
}
//End Map
//Main Structure
inline void ir() {
memset(h, ecnt = -1, sizeof(h));
scanf("%d%d", &N, &M);
N += 2;
int u, v, a, b, c, d;
for(int i = 0;i < M;++i) {
scanf("%d%d%d%d%d%d", &u, &v, &a, &b, &c, &d);
adde(u, v, b + d);
if(c > 0) adde(v, u, a - d);
}
}
const double eps = 1e-3;
int main() {
ir();
double L = 0.0, R = 1e9;
while(R - L > eps) {
double mid = (L + R) / 2.;
for(int i = 0;i <= ecnt;++i) e[i].w += mid;
if(TEST())
L = mid;
else
R = mid;
for(int i = 0;i <= ecnt;++i) e[i].w -= mid;
}
printf("%.2f\n", (L + R) / 2.);
return 0;
}