bzoj3597[Scoi2014]方伯伯運椰子 01分數規劃+spfa判負環

3597: [Scoi2014]方伯伯運椰子

Time Limit: 30 Sec  Memory Limit: 64 MB
Submit: 594  Solved: 360
[Submit][Status][Discuss]

Description

 

Input

 第一行包含二個整數N,Mphp

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

Output

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

Sample Input

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

Sample Output

103.00

HINT

 

 1<=N<=5000網絡


0<=M<=3000

1<=Ui,Vi<=N+2

0<=Ai,Bi<=500

0<=Ci<=10000

0<=Di<=1000

 

很容易就能夠想到01分數規劃,而後思考怎麼判斷是否有可行解
一次完整的修改應該是找兩條路徑,一條路徑容量擴大1,流量擴大1,另外一條流量-1,容量縮小1
若是第一條路徑上的邊爲e1,第二條路徑上的邊爲e2,代價就是 $\sum{a_{e1}}+\sum{d_{e1}}-\sum{d_{e2}}+\sum{b_{e2}}$spa

能夠發現這兩條路徑除掉前面的公共路徑以後能夠造成一個無向環,而且一條邊的擴容、縮容付出的代價是獨立的
獲得一個思路:創建雙向邊,正向邊權是擴容代價,若是容量上限不爲0,反向邊權是縮容代價,不然不建反向邊code

因爲題目的答案分式和變化的容量無直接關係,因此容量變化1和變化x是沒有區別的,直接檢查容量變化1是否可行
01規劃判斷的時候,把每一個環的權值定義爲 $邊數*mid+\sum邊權$
把每條邊權值加上mid後找負環,若是存在負環就有可行解,不然沒有blog

 1 #include<bits/stdc++.h>
 2 #define N 5010
 3 using namespace std;
 4 const double eps=1e-3;
 5 int n,m,tot,fg,hd[N],vis[N];double d[N];
 6 struct edge{int v,w,next;}e[N<<1];
 7 void adde(int u,int v,int w){
 8     e[++tot].v=v;
 9     e[tot].next=hd[u];
10     e[tot].w=w;
11     hd[u]=tot;
12 }
13 void dfs(int u,double x){
14     vis[u]=1;
15     if(fg)return;
16     for(int i=hd[u];i;i=e[i].next){
17         int v=e[i].v;
18         if(d[v]>d[u]+x+e[i].w){
19             d[v]=d[u]+x+e[i].w;
20             if(vis[v]){fg=1;return;}
21             dfs(v,x);
22         }
23     }
24     vis[u]=0;
25 }
26 bool check(double x){
27     fg=0;memset(d,0,sizeof(d));
28     memset(vis,0,sizeof(vis));
29     for(int i=1;i<=n&&!fg;i++)dfs(i,x);
30     return fg;
31 }
32 int main(){
33     scanf("%d%d",&n,&m);
34     n+=2;int u,v,a,b,c,d;
35     for(int i=1;i<=m;i++){
36         scanf("%d%d%d%d%d%d",&u,&v,&a,&b,&c,&d);
37         adde(u,v,b+d);if(c)adde(v,u,a-d);
38     }
39     double l=0,r=1e6,mid,ans;
40     while(l+eps<=r){
41         mid=(l+r)/2;
42         if(check(mid))l=ans=mid;
43         else r=mid;
44     }
45     printf("%.2lf\n",ans);
46     return 0;
47 }
相關文章
相關標籤/搜索