hdu 1533 - 最小费用最大流

题目链接:点击这里

解题思路:

首先要知道怎么写最大流,不懂的可以先转个场:浅谈网络流

那么最小费用最大流就是现在每个边不只有容量了,还有一个花费,就是单位流量流过要的花费。

要求你求出在最大流的情况下的最小花费,所以之前我们再求最大流不考虑花费的情况下,只要能达到最大流就可以了,并不用去考虑流量是走哪些边的,现在就是要考虑了。

那么在建边的时候正向边的花费就是我们求得的值,反向边就是相应的负值,因为当"反悔"的时候这条边是不走了,要把之前的花费还回去。

这题我们还需要构造一个超级源点和超级汇点,源点和人构造连接,房子和汇点构造连接就构成了一个网络流.初始化所有边的容量都是1,我们构造的连接的花费就是0了,原来的边多少花费就是多少,

求解步骤:

1.先用最短路算法(spfa,dij..想用啥用啥)在图上跑出源点到汇点的最短路径(对于花费来说的),当然边的限制跟增广路一样必须路径上的每条边残量都大于0.

2.求出这条路径的最大增大流量(mins),然后用dis[T]*mins,dis[T]就是到汇点的最短距离.这个就是当前最优增广路的花费。

3.最短路径的增广路也要跟网络流的操作一样,残量网络也要改变,即正向边-=扩增流量,反向边+=扩增流量。

4.重复上面1-3操作,直到没有最短路径的增广路,最后答案就是增广得到的花费和.

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int mx = 4e2 + 10;
typedef long long ll;
int n,m,head[mx],tot,hi[mx][2],mi[mx][2];
char str[mx][mx];
struct node
{
	int y,nxt;
	int v,c;
}edge[mx<<5];
int S,T,dis[mx],pre[mx];
bool vis[mx];
void AddEdge(int x,int y,int v,int c)
{
	edge[tot] = {y,head[x],v,c};
	head[x] = tot++;
}
bool spfa()
{
	memset(dis,inf,sizeof(dis));
	memset(pre,-1,sizeof(pre));
	queue<int> que;
	que.push(S);
	dis[S] = 0;
 	while(!que.empty()){
		int no = que.front();
		que.pop();
		vis[no] = 0;
		for(int i=head[no];~i;i=edge[i].nxt)
		{
			int y = edge[i].y;
			if(edge[i].v&&dis[y]>dis[no]+edge[i].c){
				dis[y] = dis[no] + edge[i].c;
				pre[y] = i;
				if(!vis[y]){
					que.push(y);
					vis[y] = 1; 
				}
			}
		}
	}
	return dis[T] != inf; 
}
int mincost()
{
	int ans = 0;
	while(spfa()){
		int mins = inf;
		for(int i=pre[T];~i;i=pre[edge[i^1].y]) mins = min(mins,edge[i].v);
		for(int i=pre[T];~i;i=pre[edge[i^1].y])
		{
			edge[i].v -= mins;
			edge[i^1].v += mins;
		}
		ans += mins*dis[T];
	}
	return ans;
}
int main()
{
	while(scanf("%d%d",&n,&m)&&(n+m))
	{
		memset(head,-1,sizeof(head));
		tot = 0;
		for(int i=1;i<=n;i++) scanf("%s",str[i]+1);
		int N = 0,M = 0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(str[i][j]=='m'){
					mi[++N][0] = i;
					mi[N][1] = j;
				}if(str[i][j]=='H'){
					hi[++M][0] = i;
					hi[M][1] = j;
				}
			}
		}
		S = 0, T = 2*N + 1;
		for(int i=1;i<=N;i++){
			AddEdge(S,i,1,0);
			AddEdge(i,S,0,0);
			AddEdge(i+N,T,1,0);
			AddEdge(T,i+N,0,0);
			for(int j=1;j<=N;j++){
				AddEdge(i,j+N,1,abs(mi[i][0]-hi[j][0])+abs(mi[i][1]-hi[j][1]));
				AddEdge(j+N,i,0,-abs(mi[i][0]-hi[j][0])-abs(mi[i][1]-hi[j][1]));
			}
		}
		printf("%d\n",mincost());
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/a1214034447/article/details/81137598