1. 程式人生 > >POJ 2195 Going Home (最小費用最大流)

POJ 2195 Going Home (最小費用最大流)

題目連結
第一次寫最小費用最大流
最小費用最大流相當於最短路和最大流的結合,建立了殘量圖後,費用作為距離
然後bfs遍歷源點到匯點的最小費用,用陣列記錄遍歷路徑,而後通過最大流的做法
對殘量圖進行更新,尋找路徑中的最小流量得到的就是最小費用最大流。
題目大意:圖中有若干個m和H,每個m都要回到一個H,問最少走多少步
這裡可以把走的步數定位費用
於是可以建圖
源點 連結 人 流量為1 費用為0
人 連結 家 流量為 1 費用為bfs得到的最小步數
家 連結 匯點 流量為 1 費用為 0
跑一遍最小費用最大流就可以得到答案啦

#include<stdio.h>
#include<queue> #include<string.h> #include<vector> using namespace std; #define MAXN 1005 #define INF 2000000000 int source,sink; //源點 匯點 struct tree { int from,to,flow,worth,next; //結點,流量,費用,連結串列 tree(){} tree(int fr,int ro,int fl,int wo,int ne) { from=fr,to=ro,flow=fl,worth=wo,next=ne; } }e[MAXN*MAXN]; int
g[MAXN]; // 建立連結串列 int num; //邊數 void init() //初始化 { memset(g,0,sizeof(g)); num=1; } void addtree(int from,int to,int flow,int worth) //建圖 { e[++num]=tree(from,to,flow,worth,g[from]); g[from]=num; e[++num]=tree(to,from,0,-worth,g[to]); //反向弧 g[to]=num; } bool visque[MAXN]; //檢視是否入隊
int dis[MAXN]; //最小距離 int pre[MAXN],prx[MAXN]; //記錄路線用於更新殘量圖 queue<int>q; int bfs() //尋找最短路 { while(!q.empty()) q.pop(); //初始化佇列 for(int i=0;i<=MAXN;i++) dis[i]=INF; //初始化距離 q.push(source); //源點入隊 dis[source]=0; visque[source]=true; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=g[u];i;i=e[i].next) { if(e[i].flow>0&&dis[u]+e[i].worth<dis[e[i].to]) //更新最短路 { dis[e[i].to]=dis[u]+e[i].worth; pre[e[i].to]=u; prx[e[i].to]=i; if(!visque[e[i].to]) { visque[e[i].to]=true; q.push(e[i].to); } } } visque[u]=false; //前面已經讓u出隊了所以這裡要寫一下 } return dis[sink]!=INF; //判斷是否可以到達匯點 } int dfs() { int u=sink; int ans=INF; while(u!=source) //找當前路中的最小流量 { if(e[prx[u]].flow<ans) ans=e[prx[u]].flow; u=pre[u]; } u=sink; while(u!=source) //更新殘量圖 { e[prx[u]].flow-=ans; e[prx[u]^1].flow+=ans; u=pre[u]; } return ans*dis[sink]; } int solve() { int cur=0; int ans=0; while(bfs()) { cur+=dfs(); if(cur>ans) ans=cur; } return ans; } char map[MAXN][MAXN]; int mapnum[MAXN][MAXN]; int n,m; struct Point { int x,y,w; Point(){} Point(int xx,int yy,int ww) { x=xx,y=yy,w=ww; } }; int vis[MAXN][MAXN]; int tur[4][2]={1,0,-1,0,0,1,0,-1}; void bfs_m(Point s) { queue<Point> v; v.push(s); memset(vis,0,sizeof(vis)); vis[s.x][s.y]=1; while(!v.empty()) { Point u=v.front(); v.pop(); if(map[u.x][u.y]=='H') addtree(mapnum[s.x][s.y],mapnum[u.x][u.y],1,u.w); for(int i=0;i<4;i++) { Point pp=u; pp.x+=tur[i][0]; pp.y+=tur[i][1]; if(pp.x<0||pp.y<0||pp.x>=n||pp.y>=m||vis[pp.x][pp.y])continue; vis[pp.x][pp.y]=1; pp.w++; v.push(pp); } } } int main() { while(scanf("%d%d",&n,&m)==2,n+m) { init(); sink=1000,source=1; int cout=2; for(int i=0;i<n;i++) { scanf("%s",map[i]); for(int j=0;j<m;j++) { if(map[i][j]=='.') continue; mapnum[i][j]=cout++; //給點編號利於建圖 if(map[i][j]=='m') addtree(source,mapnum[i][j],1,0); else addtree(mapnum[i][j],sink,1,0); } } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if(map[i][j]=='m') bfs_m(Point(i,j,0)); //尋找每一個人對應所有家的最短路 } } printf("%d\n",solve()); } }