poj2175费用流消圈算法
生活随笔
收集整理的這篇文章主要介紹了
poj2175费用流消圈算法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題意:
? ? ?有n個建筑,每個建筑有ai個人,有m個避難所,每個避難所的容量是bi,ai到bi的費用是|x1-x2|+|y1-y2|+1,然后給你一個n*m的矩陣,表示當前方案,問當前避難方案是否是最優的,如果不是,輸出一個比這個好的就行。
思路:
? ? 大體看一下題目,很容易想到費用流直接去弄,建圖也比較簡單,但是費用流超時,仔細看上面的最后一句,是找到一個比當前的好就行,不用最好,這樣我們可以考慮費用流的消圈,我也是今天第一次聽到這個東西,研究了將近兩個小時,大體明白了,明白后再反過來想想確實很簡單,學東西就這樣,很正常,下面說下消圈算法,消圈可以用來判斷費用流的當前狀態是否是最優的,大體是這樣,我們先建立殘余網絡,就是根據題目給的信息建出殘余圖,如果是初學建議不要向網上很多人那樣直接建立部分有用邊(初學很容易不懂),我們可以這樣,把所有的殘余網絡都補上,我寫下本題的建邊過程方便理解(正反邊分開建立):
ss -> i ?流量0 費用0 ?
? ? ? ? ?//因為跑完之后前面肯定是流量都用沒了
i -> ss 流量c ,費用0?
? ? ? ? ?//c是這個建筑有多少人,滿流的正向0,反向滿c
i -> j + n 流量INF-c 費用 w
? ? ? ? ?//w是i,j的距離+1,c是建筑里人數,本來是INF,跑完后是INF-c
j+n -> i ?流量c ,費用w
? ? ? ? ?//如上
j+n -> tt 流量q,費用0,
? ? ? ? ?//q是建筑的容量剩余,就是所有的-當前用了的,當前用的綜合自己算出來
tt -> j+n 流量p,費用0
? ? ? ? ?//p是當前這個避難所一共用了多少容量
? ? ?有n個建筑,每個建筑有ai個人,有m個避難所,每個避難所的容量是bi,ai到bi的費用是|x1-x2|+|y1-y2|+1,然后給你一個n*m的矩陣,表示當前方案,問當前避難方案是否是最優的,如果不是,輸出一個比這個好的就行。
思路:
? ? 大體看一下題目,很容易想到費用流直接去弄,建圖也比較簡單,但是費用流超時,仔細看上面的最后一句,是找到一個比當前的好就行,不用最好,這樣我們可以考慮費用流的消圈,我也是今天第一次聽到這個東西,研究了將近兩個小時,大體明白了,明白后再反過來想想確實很簡單,學東西就這樣,很正常,下面說下消圈算法,消圈可以用來判斷費用流的當前狀態是否是最優的,大體是這樣,我們先建立殘余網絡,就是根據題目給的信息建出殘余圖,如果是初學建議不要向網上很多人那樣直接建立部分有用邊(初學很容易不懂),我們可以這樣,把所有的殘余網絡都補上,我寫下本題的建邊過程方便理解(正反邊分開建立):
ss -> i ?流量0 費用0 ?
? ? ? ? ?//因為跑完之后前面肯定是流量都用沒了
i -> ss 流量c ,費用0?
? ? ? ? ?//c是這個建筑有多少人,滿流的正向0,反向滿c
i -> j + n 流量INF-c 費用 w
? ? ? ? ?//w是i,j的距離+1,c是建筑里人數,本來是INF,跑完后是INF-c
j+n -> i ?流量c ,費用w
? ? ? ? ?//如上
j+n -> tt 流量q,費用0,
? ? ? ? ?//q是建筑的容量剩余,就是所有的-當前用了的,當前用的綜合自己算出來
tt -> j+n 流量p,費用0
? ? ? ? ?//p是當前這個避難所一共用了多少容量
建圖之后從重點開始跑最短路,如果沒有發現負權值回路那么就是最優的,否則我們就隨便找到一個負權值回路,然后把i->j的邊的流量++,j->i的邊的流量--,至于為什么這樣我們可以這樣想,首先建議現在紙上大體畫一下,自己隨便找一個負權值回路,然后看看特點,從終點開始跑,發現負權值回路說明正值<負值(花費),那么我們把負值的流量-1給正值得流量+1,是不是即達到了流量平衡有減少了花費呢?還有找負權值回路的時候和費用流是一樣的,費用更小(最短路)同時有流量才能跑,如果還不懂就不停的畫,畫著畫著就懂了,還有畫的過程中記得去想費用流的反向費用是負的,最大流的反向流量就是正向流量的減少量,還有一點就是注意一下,找負環的時候,如果用的是Spfa的話,最后一個有可能不是環上的,這樣我們就得先找到一個肯定是環上的點,這個好辦,直接mark,從后往前一直找到mark過的就跳出來,當前這個肯定是環上的(不明白的話可以在紙上畫幾個6感覺下),具體看代碼。
#include<queue> #include<stdio.h> #include<string.h>#define N_node 205 #define N_edge 30000 #define INF 100000000using namespace std;typedef struct {int from ,to ,cost ,flow ,next; }STAR;typedef struct {int a ,b ,c; }NODE;STAR E[N_edge]; int list[N_node] ,tot; int C[N_node];//入隊次數 int mer[N_node];//記錄路徑 int s_x[N_node] ,mark[N_node]; int now[N_node][N_node]; NODE A[N_node] ,B[N_node];void add(int a ,int b ,int c ,int d) {E[++tot].from = a;E[tot].to = b;E[tot].cost = c;E[tot].flow = d;E[tot].next = list[a];list[a] = tot; }int abss(int x) {return x > 0 ? x : -x; }bool Spfa(int s ,int n) {for(int i = 0 ;i <= n ;i ++)s_x[i] = INF;memset(mark ,0 ,sizeof(mark));memset(C ,0 ,sizeof(C));queue<int>q; q.push(s);mark[s] = C[s] = 1 ,s_x[s] = 0;int xin ,tou;memset(mer ,255 ,sizeof(mer));while(!q.empty()){tou = q.front();q.pop();mark[tou] = 0;for(int k = list[tou] ;k ;k = E[k].next){xin = E[k].to;if(s_x[xin] > s_x[tou] + E[k].cost && E[k].flow){s_x[xin] = s_x[tou] + E[k].cost;mer[xin] = k;if(!mark[xin]){mark[xin] = 1;q.push(xin);if(++C[xin] > n) return xin;}}}}return 0; }int main () {int n ,m ,i ,j;int st[N_node];while(~scanf("%d %d" ,&n ,&m)){for(i = 1 ;i <= n ;i++)scanf("%d %d %d" ,&A[i].a ,&A[i].b ,&A[i].c);for(i = 1 ;i <= m ;i ++)scanf("%d %d %d" ,&B[i].a ,&B[i].b ,&B[i].c);memset(st ,0 ,sizeof(st));for(i = 1 ;i <= n ;i ++)for(j = 1 ;j <= m ;j ++){scanf("%d" ,&now[i][j]);st[j] += now[i][j];}memset(list ,0 ,sizeof(list));tot = 1;int ss = 0 ,tt = n + m + 1;for(i = 1 ;i <= n ;i ++)add(ss ,i ,0 ,0),add(i ,ss ,0 ,A[i].c);for(i = 1 ;i <= m ;i ++)add(i + n ,tt ,0 ,B[i].c - st[i]) ,add(tt ,i + n ,0 ,st[i]);for(i = 1 ;i <= n ;i ++)for(j = 1 ;j <= m ;j ++){add(i ,j + n ,abss(A[i].a-B[j].a)+abss(A[i].b - B[j].b) + 1 ,INF - now[i][j]);add(j + n ,i ,-(abss(A[i].a-B[j].a)+abss(A[i].b - B[j].b) + 1) ,now[i][j]);}int x = Spfa(tt ,tt);if(!x){printf("OPTIMAL\n");continue;}printf("SUBOPTIMAL\n");memset(mark ,0 ,sizeof(mark));i = mer[x];while(1)//找到一個肯定在環上的點{x = E[i].to;if(mark[x]) break;mark[x] = 1;i = mer[E[i].from];}memset(mark ,0 ,sizeof(mark));for(i = mer[x] ;i + 1 ;i = mer[E[i].from]){int a = E[i].from ,b = E[i].to;if(a >= 1 && a <= n && b >= n + 1 && b <= n + m)now[a][b-n] ++;if(a >= n + 1 && a <= n + m && b >= 1 && b <= n)now[b][a-n] --;if(mark[a] && mark[b]) break;mark[a] = mark[b] = 1;}for(i = 1 ;i <= n ;i ++)for(j = 1 ;j <= m ;j ++)if(j == m) printf("%d\n" ,now[i][j]);else printf("%d " ,now[i][j]);}return 0;}
總結
以上是生活随笔為你收集整理的poj2175费用流消圈算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ2155二维线段树
- 下一篇: poj2186强联通(牛仰慕)