Problem Descriptionc++
A coding contest will be held in this university, in a huge playground. The whole playground would be divided into N blocks, and there would be M directed paths linking these blocks. The i-th path goes from the ui-th block to the vi-th block. Your task is to solve the lunch issue. According to the arrangement, there are si competitors in the i-th block. Limited to the size of table, bi bags of lunch including breads, sausages and milk would be put in the i-th block. As a result, some competitors need to move to another block to access lunch. However, the playground is temporary, as a result there would be so many wires on the path.
For the i-th path, the wires have been stabilized at first and the first competitor who walker through it would not break the wires. Since then, however, when a person go through the i - th path, there is a chance of pi to touch
the wires and affect the whole networks. Moreover, to protect these wires, no more than ci competitors are allowed to walk through the i-th path.
Now you need to find a way for all competitors to get their lunch, and minimize the possibility of network crashing.git
Input算法
The first line of input contains an integer t which is the number of test cases. Then t test cases follow.
For each test case, the first line consists of two integers N (N ≤ 100) and M (M ≤ 5000). Each of the next N lines contains two integers si and bi (si , bi ≤ 200).
Each of the next M lines contains three integers ui , vi and ci(ci ≤ 100) and a float-point number pi(0 < pi < 1).
It is guaranteed that there is at least one way to let every competitor has lunch.ide
Outputui
For each turn of each case, output the minimum possibility that the networks would break down. Round it to 2 digits.this
Sample Inputspa
1 4 4 2 0 0 3 3 0 0 3 1 2 5 0.5 3 2 5 0.5 1 4 5 0.5 3 4 5 0.5code
Sample Outputblog
0.50three
思路:
這是一個最小費用最大流的變形題目。
要仔細讀懂題目,題目要求的是崩潰的最小可能性。因爲崩潰多是由一條邊或多條邊一塊兒的,因此正着求崩潰機率很難求,咱們反着思考,用1去減每條邊都不引發圖崩潰的機率即爲崩潰的機率。
因爲求的是最小,並且邊有容量限制,咱們很天然的想到了最小費用最大流(對於不崩潰的機率應該是最大費用最大流)
建圖時,某個點有人則想s連一條容量爲人數的邊,有食物則向t連一條容量爲是無數的邊,其餘的邊按照題目的數據相連便可,這個很好處理。
還有一個條件是某條路第一遍走的時候是沒有花費的。個人處理方法是將一條容量爲c,花費爲w的邊拆成兩條邊:容量爲c-1,花費爲w的一條邊;容量爲1,花費爲1.0的一條邊(花費爲1.0是爲了確保不會對最終結果產生影響)。
細節不少,因爲機率要想乘因此初始化時要注意,算一條增廣路徑對總結果的貢獻時也要注意是乘方而不是簡單的相乘。
代碼1:求增廣路徑時直接求最長路
View Code1 /* 2 * @FileName: D:\代碼與算法\2017訓練比賽\2016青島區域賽\g.cpp 3 * @Author: Pic 4 * @Created Time: 2017/10/7 12:46:48 5 */ 6 #include <bits/stdc++.h> 7 using namespace std; 8 const double INF = -1*10.0; 9 const int maxn=200+10; 10 11 struct Edge 12 { 13 int from,to,cap,flow; 14 double cost; 15 Edge(){} 16 Edge(int f,int t,int c,int fl,double co):from(f),to(t),cap(c),flow(fl),cost(co){} 17 }; 18 19 struct MCMF 20 { 21 int n,m,s,t; 22 vector<Edge> edges; 23 vector<int> G[maxn]; 24 bool inq[maxn]; //是否在隊列 25 double d[maxn]; //Bellman_ford單源最短路徑 26 int p[maxn]; //p[i]表從s到i的最小費用路徑上的最後一條弧編號 27 int a[maxn]; //a[i]表示從s到i的最小殘量 28 29 //初始化 30 void init(int n,int s,int t) 31 { 32 this->n=n, this->s=s, this->t=t; 33 edges.clear(); 34 for(int i=0;i<n;++i) G[i].clear(); 35 } 36 37 //添加一條有向邊 38 void AddEdge(int from,int to,int cap,double cost) 39 { 40 edges.push_back(Edge(from,to,cap,0,cost)); 41 edges.push_back(Edge(to,from,0,0,-cost)); 42 m=edges.size(); 43 G[from].push_back(m-2); 44 G[to].push_back(m-1); 45 } 46 47 //求一次增廣路 48 bool BellmanFord(int &flow, double &cost) 49 { 50 for(int i=0;i<n;++i) d[i]=INF; 51 memset(inq,0,sizeof(inq)); 52 d[s]=1.0, a[s]=1e9+30, inq[s]=true, p[s]=0; 53 queue<int> Q; 54 Q.push(s); 55 while(!Q.empty()) 56 { 57 //cout<<"Q"<<endl; 58 int u=Q.front(); Q.pop(); 59 inq[u]=false; 60 for(int i=0;i<G[u].size();++i) 61 { 62 Edge &e=edges[G[u][i]]; 63 double tmp=(d[u]*e.cost); 64 //tmp=-1*fabs(tmp); 65 if( e.cap>e.flow && d[e.to] < tmp ) 66 { 67 d[e.to]=tmp; 68 p[e.to]=G[u][i]; 69 a[e.to]= min(a[u],e.cap-e.flow); 70 if(!inq[e.to]){ Q.push(e.to); inq[e.to]=true; } 71 } 72 } 73 } 74 //cout<<d[t]<<endl; 75 //if(fabs(d[t]-INF)<1e-3) return false; 76 if(d[t]<0) return false; 77 flow += a[t]; 78 for(int i=0;i<a[t];i++) cost*=(d[t]); 79 int u=t; 80 while(u!=s) 81 { 82 edges[p[u]].flow += a[t]; 83 edges[p[u]^1].flow -=a[t]; 84 u = edges[p[u]].from; 85 } 86 return true; 87 } 88 89 //求出最小費用最大流 90 double Min_cost() 91 { 92 int flow=0;double cost=1.0; 93 while(BellmanFord(flow,cost)) ; 94 return cost; 95 } 96 }MM; 97 int main() 98 { 99 // freopen("data.in","r",stdin); 100 //freopen("data.out","w",stdout); 101 int t; 102 scanf("%d",&t); 103 while(t--){ 104 int n,m; 105 scanf("%d%d",&n,&m); 106 MM.init(n+2,0,n+1); 107 int x,y; 108 for(int i=1;i<=n;i++){ 109 scanf("%d%d",&x,&y); 110 if(x!=0) 111 MM.AddEdge(0,i,x,1.0); 112 if(y!=0) 113 MM.AddEdge(i,n+1,y,1.0); 114 } 115 int u,v,c;double w; 116 for(int i=1;i<=m;i++){ 117 scanf("%d%d%d%lf",&u,&v,&c,&w); 118 //MM.AddEdge(u,v,c,w); 119 if(c>0){ 120 MM.AddEdge(u,v,c-1,1.0-w); 121 MM.AddEdge(u,v,1,1.0); 122 } 123 } 124 double res=fabs(MM.Min_cost()); 125 //cout<<res<<endl; 126 res=1.0-res; 127 if(res>=1.0) res=1.0; 128 printf("%.2lf\n",res); 129 } 130 return 0; 131 }代碼2:花費取負,求最小費用最大流,注意初始化的值
View Code1 /* 2 * @FileName: D:\代碼與算法\2017訓練比賽\2016青島區域賽\g.cpp 3 * @Author: Pic 4 * @Created Time: 2017/10/7 12:46:48 5 */ 6 #include <bits/stdc++.h> 7 using namespace std; 8 const double INF = 1e8; 9 const int maxn=200+10; 10 11 struct Edge 12 { 13 int from,to,cap,flow; 14 double cost; 15 Edge(){} 16 Edge(int f,int t,int c,int fl,double co):from(f),to(t),cap(c),flow(fl),cost(co){} 17 }; 18 19 struct MCMF 20 { 21 int n,m,s,t; 22 vector<Edge> edges; 23 vector<int> G[maxn]; 24 bool inq[maxn]; //是否在隊列 25 double d[maxn]; //Bellman_ford單源最短路徑 26 int p[maxn]; //p[i]表從s到i的最小費用路徑上的最後一條弧編號 27 int a[maxn]; //a[i]表示從s到i的最小殘量 28 29 //初始化 30 void init(int n,int s,int t) 31 { 32 this->n=n, this->s=s, this->t=t; 33 edges.clear(); 34 for(int i=0;i<n;++i) G[i].clear(); 35 } 36 37 //添加一條有向邊 38 void AddEdge(int from,int to,int cap,double cost) 39 { 40 edges.push_back(Edge(from,to,cap,0,cost)); 41 edges.push_back(Edge(to,from,0,0,-cost)); 42 m=edges.size(); 43 G[from].push_back(m-2); 44 G[to].push_back(m-1); 45 } 46 47 //求一次增廣路 48 bool BellmanFord(int &flow, double &cost) 49 { 50 for(int i=0;i<n;++i) d[i]=INF; 51 memset(inq,0,sizeof(inq)); 52 d[s]=1.0, a[s]=INF, inq[s]=true, p[s]=0; 53 queue<int> Q; 54 Q.push(s); 55 while(!Q.empty()) 56 { 57 // cout<<"Q"<<endl; 58 int u=Q.front(); Q.pop(); 59 inq[u]=false; 60 for(int i=0;i<G[u].size();++i) 61 { 62 Edge &e=edges[G[u][i]]; 63 double tmp=(d[u]*e.cost); 64 tmp=-1*fabs(tmp); 65 if( e.cap>e.flow && d[e.to] > tmp ) 66 { 67 d[e.to]=tmp; 68 p[e.to]=G[u][i]; 69 a[e.to]= min(a[u],e.cap-e.flow); 70 if(!inq[e.to]){ Q.push(e.to); inq[e.to]=true; } 71 } 72 } 73 } 74 if(fabs(d[t]-INF)<1e3) return false; 75 flow += a[t]; 76 for(int i=0;i<a[t];i++) cost*=(d[t]); 77 int u=t; 78 while(u!=s) 79 { 80 edges[p[u]].flow += a[t]; 81 edges[p[u]^1].flow -=a[t]; 82 u = edges[p[u]].from; 83 } 84 return true; 85 } 86 87 //求出最小費用最大流 88 double Min_cost() 89 { 90 int flow=0;double cost=1.0; 91 while(BellmanFord(flow,cost)); 92 return cost; 93 } 94 }MM; 95 int main() 96 { 97 // freopen("data.in","r",stdin); 98 //freopen("data.out","w",stdout); 99 int t; 100 scanf("%d",&t); 101 while(t--){ 102 int n,m; 103 scanf("%d%d",&n,&m); 104 MM.init(n+2,0,n+1); 105 int x,y; 106 for(int i=1;i<=n;i++){ 107 scanf("%d%d",&x,&y); 108 if(x!=0) 109 MM.AddEdge(0,i,x,1.0); 110 if(y!=0) 111 MM.AddEdge(i,n+1,y,1.0); 112 } 113 int u,v,c;double w; 114 for(int i=1;i<=m;i++){ 115 scanf("%d%d%d%lf",&u,&v,&c,&w); 116 //MM.AddEdge(u,v,c,w); 117 if(c>0){ 118 MM.AddEdge(u,v,c-1,w-1.0); 119 MM.AddEdge(u,v,1,1.0); 120 } 121 } 122 double res=fabs(MM.Min_cost()); 123 //cout<<res<<endl; 124 res=1.0-res; 125 if(res>=1.0) res=1.0; 126 printf("%.2lf\n",res); 127 } 128 return 0; 129 }