1. 程式人生 > >最小費用最大流(講解+模板)

最小費用最大流(講解+模板)



        問題引入:最小費用最大流問題是經濟學和管理學中的一類典型問題。在一個網路中每段路徑都有“容量”和“費用”兩個限制的條件下,此類問題的研究試圖尋找出:流量從A到B,如何選擇路徑、分配經過路徑的流量,可以在流量最大的前提下,達到所用的費用最小的要求。如n輛卡車要運送物品,從A地到B地。由於每條路段都有不同的路費要繳納,每條路能容納的車的數量有限制,最小費用最大流問題指如何分配卡車的出發路徑可以達到費用最低,物品又能全部送到。

       解決最小費用最大流問題,一般有兩條途徑。一條途徑是先用最大流演算法算出最大流,然後根據邊費用,檢查是否有可能在流量平衡的前提下通過調整邊流量,使總費用得以減少?只要有這個可能,就進行這樣的調整。調整後,得到一個新的最大流。

      然後,在這個新流的基礎上繼續檢查,調整。這樣迭代下去,直至無調整可能,便得到最小費用最大流。這一思路的特點是保持問題的可行性(始終保持最大流),向最優推進。另一條解決途徑和前面介紹的最大流演算法思路相類似,一般首先給出零流作為初始流。這個流的費用為零,當然是最小費用的。然後尋找一條源點至匯點的增流鏈,但要求這條增流鏈必須是所有增流鏈中費用最小的一條。如果能找出增流鏈,則在增流鏈上增流,得出新流。將這個流做為初始流看待,繼續尋找增流鏈增流。這樣迭代下去,直至找不出增流鏈,這時的流即為最小費用最大流。這一演算法思路的特點是保持解的最優性(每次得到的新流都是費用最小的流),而逐漸向可行解靠近(直至最大流時才是一個可行解)。

    由於第二種演算法和已介紹的最大流演算法接近,且演算法中尋找最小費用增流鏈,可以轉化為一個尋求源點至匯點的最短路徑問題,所以這裡介紹這一演算法。

【計算步驟】 ⒈ 對網路G=[V,E,C,W],給出流值為零的初始流。 ⒉ 作伴隨這個流的增流網路G′=[V′,E′,W′]。G′的頂點同G:V′=V。若G中f(u,v)0,則G′中建邊(v,u),w′(v,u)=-w(u,v)。 ⒊ 若G′不存在x至y的路徑,則G的流即為最小費用最大流, 停止計算;否則用標號法找出x至y的最短路徑P。 ⒋ 根據P,在G上增流:對P的每條邊(u,v),若G存在(u,v),則(u,v)增流;若G存在(v,u),則(v,u)減流。增(減)流後,應保證對任一邊有c(e)≥ f(e)≥0。 ⒌ 根據計算最短路徑時的各頂點的標號值L(v),按下式修 改G一切邊的權數w(e): L(u)-L(v)+w(e)→w(e)。 ⒍ 將新流視為初始流,轉2。 這裡仍然採用EK演算法,雖然複雜度較高,但是一般問題還是可以解決的。 和最大流演算法的唯一區別是假如了最短路spfa演算法(相信不難理解) 舉一個模板題程式碼說明問題 程式碼如下:
#include<stdio.h>
#include<string.h>
#include<queue>
#include<stdlib.h>
#include<math.h>
#include<limits.h>
#include<algorithm>
using namespace  std;
#define maxn 205
struct node
{
	int x,y;
}hdis[maxn],pdis[maxn];
struct road
{
	int flow;
	int cost;
}path[maxn][maxn];
char s[maxn][maxn];
int pre[maxn];
int link[maxn];
int  used[maxn];
int best[maxn];
int ans,n,m;
int spfa();
void operat();
void operat()
{
       ans=0;
       int end=n+m+1;
	  while(spfa())
	  {
          int x=end;
          int dis=INT_MAX;
		  while(x)
		  {
		  	  int vlink=link[x];
		  	  dis=min(dis,path[vlink][x].flow);
		  	  x=vlink;
		  }	
		  x=end;
		  while(x)
		  {
		  	  int vlink=link[x];
		  	  path[vlink][x].flow-=dis;
		  	  path[x][vlink].flow+=dis;
		  	  ans+=dis*path[vlink][x].cost;
		  	  x=vlink;
		  }      	
	  }	
	  printf("%d\n",ans);
}
int spfa()
{
	int start=0,end=n+m+1;
	memset(used,0,sizeof(used));
	memset(link,-1,sizeof(link));
	memset(best,1,sizeof(best));
	queue<int>q;
	q.push(0);
	best[0]=0;
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		for(int i=1;i<=end;i++)
		{
			if(path[now][i].flow>0 && best[i]>best[now]+path[now][i].cost)
			{
			    best[i]=path[now][i].cost+best[now];
			    link[i]=now;
			    if(!used[i])
			    {
			    	used[i]=1;
			    	q.push(i);
				}
			}
		}
		used[now]=0;
	}
	if(best[end]<=100000)
	    return 1;
	    return 0;
}
int  main()
{
	int i,j;
	while(scanf("%d%d",&n,&m),n!=0 && m!=0)
	{
		memset(path,0,sizeof(path));
		int a=0,b=0;
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=m;j++)
			{
				scanf(" %c",&s[i][j]);
				if(s[i][j]=='H')
				 {
				 	hdis[++b].x=i;
				 	hdis[b].y=j;
				 }
				 if(s[i][j]=='m')
				 {
				 	pdis[++a].x=i;
				 	pdis[a].y=j;
				 }
			}
		}
		n=a;m=b;
		int x;
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=m;j++)
			{
				x=abs(hdis[j].x-pdis[i].x)+abs(hdis[j].y-pdis[i].y);
				path[i][j+n].flow=1;
				path[i][j+n].cost=x;
				path[j+n][i].cost=-x;
			}
		}
	   for(i=1;i<=n;i++)
	   	    path[0][i].flow=1;
	   for(i=1;i<=m;i++)
	        path[n+i][n+m+1].flow=1;
           operat();
	}
}