題目連接:http://poj.org/problem?id=2135ios
在最大流網絡中,每條邊只有一個限制條件,例如容量,帶寬等,這是「最小性參數」,如今加上一個新的限制條件,例如費用,這是「可加性參數」。在兩個限制條件的基礎上就引出了最小費用最大流的問題:當流量爲F時求費用最小的流;若是沒有指定F,就是求最大流時的最小費用。通常的思路就是從零流開始,每次增長一個最小費用路徑,通過屢次增廣,直到沒法再增長路徑,就獲得了最大流,通常最短路徑的尋找都是用SPFA,由於在構建成的網絡中是有負邊權的。SPFA+最大流=最小費用最大流。算法
關於最短路,其實最短路就是費用流的一種特例,單源最短路問題就是費用流將邊的容量設置爲1,cost設置爲路徑長度。在添加一個源點src,src到起點的變容量設置爲1,cost設置爲0,那麼s到終點的最小費用最大流就是單源最短路徑的解。這裏設置邊容量爲1就是想讓這條邊只通過一次,事實上這是在最短路中原本就只能通過一次。網絡
最小費用最大流中反向邊的邊權設置爲0,可是他的費用是-cost,就至關於最大流中的反向邊值同樣,反向邊就至關於將原來已經流過的流在返回回去,至關於這條支路沒有流經過,可是費用邊的反向邊就至關於這個邊的cost沒有花費,因此邊權值是負的,至關於將原來已經算入的cost再減去。優化
本題的題目環境是這樣的,有一張無向圖,起點是1,終點是n,每條邊都是有邊權的,問存不存在兩條從起點到終點的道路,使得總路程是最小的,可是兩條道路沒有重合。其實就是一個肯定流量的最小費用問題,由於每條邊用過就不能再用,這和最大流的流量等於一個邊的容量以後這條邊就不能使用本質上是同樣的,再結合邊的權值,咱們就知道這是一個最小費用肯定流問題。只要建超級源和超級匯,而且流量爲2就能夠。注意超級源和起點之間的邊的容量是2,花費是0,終點與超級匯之間的容量是2,花費是0。題目中保證流爲2的時候必定存在兩條路。spa
在個人代碼中有一個地方要說明一下:一個是保存的方式,用領接表存邊,而且在邊的結構體中就設置了(u,v)的反向邊在e[v]容器中的編號,這樣在查找增廣路上的流量的時候就很方便了,對於更新殘餘網絡也是十分的簡潔。code
代碼以下:blog
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5 using namespace std; 6 const int maxn =1005; 7 #define inf 0x3f3f3f3f 8 int dis[maxn],pre[maxn],preve[maxn]; 9 int n,m; 10 struct edge{ 11 int to,cost,capacity,rev;//rev保存的是反向邊中的第幾條是他的前向邊 12 edge(int t,int c,int cp,int re):to(t),cost(c),capacity(cp),rev(re){} 13 }; 14 vector<edge> e[maxn];//領接表存邊,e[i]存第i個結點鏈接的全部的邊 15 void addedge(int from,int to,int cost,int capacity)//一個有向邊分紅兩個 16 { 17 e[from].push_back(edge(to,cost,capacity,e[to].size()));//保存的是下一個要加入的邊 18 e[to].push_back(edge(from,-cost,0,e[from].size()-1));//反向邊初始時刻跟最大流同樣,流的大小爲0 19 } 20 bool SPFA(int s,int t,int cnt)//基本就是SPFA板尋找一條最短增廣路 21 { 22 bool inq[maxn];//spfa算法隊列優化 23 memset(pre,-1,sizeof(pre)); 24 for(int i=1;i<=cnt;i++) 25 { 26 dis[i]=inf; 27 inq[i]=false; 28 } 29 dis[s]=0; 30 queue<int> q; 31 q.push(s); 32 while(!q.empty()) 33 { 34 int u=q.front(); 35 q.pop(); 36 inq[u]=0; 37 for(int i=0;i<e[u].size();i++) 38 { 39 if(e[u][i].capacity>0) 40 { 41 int v=e[u][i].to; 42 int cost=e[u][i].cost; 43 if(dis[u]+cost<dis[v]) 44 { 45 dis[v]=dis[u]+cost; 46 pre[v]=u;//保存v的前驅結點 47 preve[v]=i;//表示u的第i條邊到達v,便於回溯 48 if(!inq[v]) 49 { 50 q.push(v); 51 inq[v]=1; 52 } 53 } 54 } 55 } 56 } 57 return dis[t]!=inf; 58 } 59 int mincost(int s,int t,int cnt)//cnt是傳的點的數量 ,與最大流的更新過程基本徹底相同 60 { 61 int cost=0; 62 while(SPFA(s,t,cnt))//SPFA找到一條最短路徑 63 { 64 int v=t,flow=inf; 65 while(pre[v]!=-1)//尋找最大流 66 { 67 int u=pre[v],i=preve[v];//回溯找回以前的點和以前的邊 68 flow=min(flow,e[u][i].capacity) ; 69 v=u;//回溯過程 70 } 71 v=t; 72 while(pre[v]!=-1)//更新殘餘網絡 73 { 74 int u=pre[v],i=preve[v]; 75 e[u][i].capacity-=flow; 76 e[v][e[u][i].rev].capacity+=flow;//更新反向邊 77 v=u;//回溯 78 } 79 cost+=dis[t]*flow;//費用累加,若是程序須要輸出最大流,能夠再累加flow 80 } 81 return cost; 82 } 83 int main() 84 { 85 while(scanf("%d%d",&n,&m)!=EOF) 86 { 87 for(int i=0;i<maxn-1;i++)e[i].clear(); 88 for(int i=1;i<=m;i++) 89 { 90 int u,v,w; 91 scanf("%d%d%d",&u,&v,&w); 92 addedge(u,v,w,1); 93 addedge(v,u,w,1);//兩個無向邊分紅兩條有向邊存儲,因此實際上將一條無向邊分紅了四條邊來存儲 94 } 95 int s=n+1,t=n+2;//設置超級源和超級匯 96 addedge(s,1,0,2); 97 addedge(n,t,0,2);//肯定最大流量就是2,就是在肯定流量條件下的最小費用問題 98 printf("%d\n",mincost(s,t,n+2));//點數加上兩個以後變成n+2 99 } 100 }