网络流之最小费用最大流
题目链接:https://cn.vjudge.net/contest/68128#problem/D
具体思路:建好图之后,每一次从源点到汇点走最短路,如果能走到就加上,如果走不到就停止。具体注意细节在代码中解释。
AC代码:
#include<iostream>
#include<string>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<stdio.h>
#include<algorithm>
using namespace std;
# define inf 0x3f3f3f3f
# define ll long long
# define maxn 100000+10
struct point
{
int x,y;
} H[maxn],M[maxn];
char str[100][100];
int head[maxn];//前向星的建立。
int num;
int pree[maxn];//记录该点前一个连的点。
int prev[maxn];//记录该点位于前向星的哪一条边上。
int vis[maxn];
int dist[maxn];//记录距离
struct node
{
int to;
int w;
int cost;
int nex;
} edge[maxn];
void addage(int fr,int to,int w,int cost)
{
edge[num].to=to;
edge[num].w=w;
edge[num].cost=cost;
edge[num].nex=head[fr];
head[fr]=num++;
edge[num].to=fr;//反向边的建立,方向相反,流量为0,话费为原来花费的负数。
edge[num].w=0;
edge[num].cost=-cost;
edge[num].nex=head[to];
head[to]=num++;
}
bool spfa(int st,int ed)
{
memset(vis,0,sizeof(vis));
memset(dist,inf,sizeof(dist));
memset(pree,-1,sizeof(pree));
queue<int>q;
vis[st]=1;
dist[st]=0;
q.push(st);
while(!q.empty())
{
int top=q.front();
q.pop();
vis[top]=0;
for(int i=head[top]; i!=-1; i=edge[i].nex)
{
int temp=edge[i].to;
if(edge[i].w>0&&dist[temp]>dist[top]+edge[i].cost)//求最短路的过程,注意dist数组里存的是花费,不是流量。
{
dist[temp]=dist[top]+edge[i].cost;
pree[temp]=top;
prev[temp]=i;
if(vis[temp]==0)
{
vis[temp]=1;
q.push(temp);
}
}
}
}
return pree[ed]!=-1;
}
int mincostflow(int st,int ed)
{
int ans=0;
while(spfa(st,ed))
{
int minn=inf;
for(int i=ed; i!=st; i=pree[i])
{
minn=min(minn,edge[prev[i]].w);
}
ans+=dist[ed]*minn;
for(int i=ed; i!=st; i=pree[i])
{
edge[prev[i]].w-=minn;
edge[prev[i]^1].w+=minn;
}
}
return ans;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)&&(m+n))
{
num=0;
memset(head,-1,sizeof(head));
for(int i=1; i<=n; i++)
{
scanf("%s",str[i]+1);
}
int t1=0,t2=0;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
if(str[i][j]=='H')
{
H[++t1].x=i;
H[t1].y=j;
}
else if(str[i][j]=='m')
{
M[++t2].x=i;
M[t2].y=j;
}
}
}
for(int i=1; i<=t1; i++)
{
for(int j=1; j<=t1; j++)
{
addage(i,t1+j,1,abs(M[i].x-H[j].x)+abs(M[i].y-H[j].y));
}
}
int st=0,ed=t1+t1+1;
for(int i=1; i<=t1; i++)
{
addage(st,i,1,0);//源点到学生流量为1,花费为0
}
for(int i=1; i<=t1; i++)
{
addage(t1+i,ed,1,0);//同理
}
int ans=mincostflow(st,ed);
printf("%d\n",ans);
}
return 0;
}