1. 程式人生 > >[SCOI2014]方伯伯運椰子

[SCOI2014]方伯伯運椰子

void main text 輸出格式 getchar() 它的 最大流 二分 math

題目描述

四川的方伯伯為了致富,決定引進海南的椰子樹。方伯伯的椰子園十分現代化,椰子園中有一套獨特的交通系統。

現在用點來表示交通節點,邊來表示道路。這樣,方伯伯的椰子園就可以看作一個有 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最大能是多少?

註:總費用 = 交通網絡的運輸花費 + 調整的花費

輸入輸出格式

輸入格式:

第一行包含二個整數N,M接下來M行代表M條邊,表示這個交通網絡每行六個整數,表示Ui,Vi,Ai,Bi,Ci,Di接下來一行包含一條邊,表示連接起點的邊

輸出格式:

一個浮點數,保留二位小數。表示答案,數據保證答案大於0

輸入輸出樣例

輸入樣例#1:
5 10
1 5 13 13 0 412
2 5 30 18 396 148
1 5 33 31 0 39
4 5 22 4 0 786
4 5 13 32 0 561
4 5 3 48 0 460
2 5 32 47 604 258
5 7 44 37 75 164
5 7 34 50 925 441
6 2 26 38 1000 22
輸出樣例#1:
103.00

說明

 1<=N<=5000
0<=M<=3000
1<=Ui,Vi<=N+2
0<=Ai,Bi<=500
0<=Ci<=10000
0<=Di<=1000

解:
分數規劃+負環
我們一開始會想到網絡流。

首先我們假設圖中一開始流量為0,目前我們處於增廣階段,那麽對於題目中的兩種調整方式
壓縮 就相當於增廣時退流
擴容 就相當於不斷增廣
因為要
“調整之後,必須要保持每條道路滿負荷,且總交通量不會減少”,那麽單純擴容顯然是不會更優的
可能存在的更優方案為 在保證最大流不變的情況下使得壓縮的擴容的總費用加起來最少。
也就是說存在如下殘量網絡:
擴容 u -> v 花費 b+d
壓縮 (即相當於反向邊有流量的話) 滿足 c>0 時 v -> u 花費 -(b-a) 即a-b



然後我們考慮對問題求解,(這一看就是分數規劃) 我們就想起了二分。
假設當前答案為 ans ,令 Z=X-Y
那麽不優的情況一定滿足 Z >= ans * k 也就是 Z - ans * k >=0 ==> ans * k -Z <= 0
Z=X-Y 也就是說 Z=調整之前的總費用是 -
調整後的總費用

也就是 Z=Σ我們在殘量網絡中走過的邊權和 k=我們在殘量網絡中經過的點
到這裏,我們要知道一個定理。

消圈定理

所謂消圈定理,就是在某個流 ff 中,如果其對應的殘余網絡沒有負圈(剩余流量為 00 的邊視為不存在),

那它一定就是當前流量下的最小費用流。

反之亦然。即「ff 是最小費用流等價於其殘余網絡中沒有負圈」。

有了這個定理,我們就好做了。
我們二分ans,將每條邊邊權加上ans(把Σ降掉),判斷是否存在負環
搞定。

還不懂得看代碼吧。

技術分享圖片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<queue>
 6 #define ll long long
 7 #define DB double
 8 #define eps 1e-3
 9 using namespace std;
10 inline int read()
11 {
12     int x=0,w=1;char ch=getchar();
13     while(!isdigit(ch)){if(ch==-) w=-1;ch=getchar();}
14     while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-0,ch=getchar();
15     return x*w;
16 }
17 const int N=1e6+10;
18 struct node{
19     int u,v;
20     DB c;
21     int ne;
22     DB w;
23 }e[N];
24 int tot,h[N],n,m;
25 void add(int u,int v,DB c)
26 {
27     tot++;e[tot]=(node){u,v,c,h[u],0};h[u]=tot;
28 }
29 int ui,vi,ai,bi,ci,di;
30 DB L,R,mid,ans,d[N];
31 bool v[N],fg;
32 void spfa(int u)
33 {
34     if(fg) return;
35     v[u]=1;
36     for(int i=h[u];i;i=e[i].ne)
37     {
38         int rr=e[i].v;
39         if(d[rr]>d[u]+e[i].w)
40         {
41             d[rr]=d[u]+e[i].w;
42             if(v[rr] || fg==1){fg=1;break;}
43             spfa(rr);
44         }
45     }
46     v[u]=0;
47 }
48 bool ok()
49 {
50     for(int i=1;i<=tot;++i) e[i].w=e[i].c+mid;
51     fg=0;
52     for(int i=1;i<=n;++i) v[i]=0,d[i]=0;
53     for(int i=1;i<=n;++i)
54     {
55         spfa(i);
56         if(fg) break;
57     }
58     return fg;
59 }
60 int main()
61 {
62     n=read();m=read();
63     for(int i=1;i<=m;++i)
64     {
65         scanf("%d%d%d%d%d%d",&ui,&vi,&ai,&bi,&ci,&di);
66         add(ui,vi,(DB)(bi+di));
67         if(ci>0)add(vi,ui,(DB)(ai-di));
68     }
69     n+=2;
70     L=0;R=10000000000;
71     while(L+eps<=R)
72     {
73         mid=(L+R)/2.0;
74         if(ok()) L=mid+eps,ans=mid;
75         else R=mid-eps;
76     }
77     printf("%.2lf",ans);
78     return 0;
79 }
View Code

(?′?‵?)I L???????








[SCOI2014]方伯伯運椰子