POJ 2195 Going Home (最小費用最大流)
阿新 • • 發佈:2019-01-31
題目連結
第一次寫最小費用最大流
最小費用最大流相當於最短路和最大流的結合,建立了殘量圖後,費用作為距離
然後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());
}
}