您的位置:首页 > 其它

POJ 2516 Minimum Cost 最小费用最大流

2014-02-15 21:16 537 查看
先说一下,第一道最小费用最大流,这些东西学习了别人的东西,尤其是这个题意,真的很不太好懂啊。

题目的意思是百度的啊:有N个供应商,M个店主,K种物品。每个供应商对每种物品的的供应量已知,每个店主对每种物品的需求量的已知,从不同的供应商运送不同的货物到不同的店主手上需要不同的花费,又已知从供应商Mj送第kind种货物的单位数量到店主Ni手上所需的单位花费。

还从别人那里搞来一张图片,一看就知道数据的意思了啊。



图片来源:http://blog.csdn.net/lyy289065406/article/details/6742534

说一下最小费用最大流问题,网上也有很多的介绍,就是先求最大流再求最小费用或者是先求最小费用然后再找到最大流。算法的意思学过网络流之后感觉理解起来,轻松一点了啊。但是还是需要多做题来进行巩固。

这是别人写的一些东西先抄过来,以后复习的时候好好看看:

来源:http://blog.csdn.net/hqd_acm/article/details/5921611

【算法思路】

    解决最小费用最大流问题,一般有两条途径。一条途径是先用最大流算法算出最大流,然后根据边费用,检查是否有可能在流量平衡的前提下通过调整边流量,使总费用得以减少?只要有这个可能,就进行这样的调整。调整后,得到一个新的最大流。 

   然后,在这个新流的基础上继续检查,调整。这样迭代下去,直至无调整可能,便得到最小费用最大流。这一思路的特点是保持问题的可行性(始终保持最大流),向最优推进。另一条解决途径和前面介绍的最大流算法思路相类似,一般首先给出零流作为初始流。这个流的费用为零,当然是最小费用的。然后寻找一条源点至汇点的增流链,但要求这条增流链必须是所有增流链中费用最小的一条。如果能找出增流链,则在增流链上增流,得出新流。将这个流做为初始流看待,继续寻找增流链增流。这样迭代下去,直至找不出增流链,这时的流即为最小费用最大流。这一算法思路的特点是保持解的最优性(每次得到的新流都是费用最小的流),而逐渐向可行解靠近(直至最大流时才是一个可行解)。 

  由于第二种算法和已介绍的最大流算法接近,且算法中寻找最小费用增流链,可以转化为一个寻求源点至汇点的最短路径问题,所以这里介绍这一算法。


            


   在这一算法中,为了寻求最小费用的增流链,对每一当前流,需建立伴随这一网络流的增流网络。例如图 1 网络G 是具有最小 费用的流,边旁参数为c(e) , f(e) , w(e),而图 2 即为该网络流 的增流网络G′。增流网络的顶点和原网络相同。 按以下原则建 立增流网络的边:若G中边(u,v)流量未饱,即f(u,v) < e(u,v),则G ' 中建边(u,v),赋权w ' (u,v)=w(u,v);若G中边(u, v)已有流量,即f(u,v)〉0,则G′中建边(v,u),赋权w′(v,u) =-w(u,v)。建立增流网络后,即可在此网络上求源点至汇点的最短路径,以此决定增流路径,然后在原网络上循此路径增流。这里,运用的仍然是最大流算法的增流原理,唯必须选定最小费用的增流链增流。 

    计算中有一个问题需要解决。这就是增流网络G ′中有负权边,因而不能直接应用标号法来寻找x至y的最短路径,采用其它计算有负权边的网络最短路径的方法来寻找x至y的最短路径,将 大大降低计算效率。为了仍然采用标号法计算最短路径,在每次建立增流网络求得最短路径后,可将网络G的权w(e)做一次修正,使再建的增流网络不会出现负权边,并保证最短路径不至于因此而改变。下面介绍这种修改方法。 

当流值为零,第一次建增流网络求最短路径时,因无负权边,当然可以采用标号法进行计算。为了使以后建立增流网络时不出现负权边,采取的办法是将 G中有流边(f(e)>0)的权w(e)修正为0。为此, 每次在增流网络上求得最短路径后,以下式计算G中新的边权w " (u,v):

w " (u,v)=L(u)-L(v)+w(u,v) (*)

  式中 L(u),L(v) -- 计算G′的x至y最短路径时u和v的标号值。第一次求最短径时如果(u,v)是增流路径上的边, 则据最短 路径算法一定有 L(v)=L(u)+w ' (u,v)=L(u)+w(u,v), 代入(*)式必有

w″(u,v)=0。

如果(u,v)不是增流路径上的边,则一定有: 

    L(v)≤L(u)+w(u,v), 

代入(*)式则有 w(u,v)≥0。

  可见第一次修正w(e)后,对任一边,皆有w(e)≥0, 且有流 的边(增流链上的边),一定有w(e)=0。以后每次迭代计算,若 f(u,v)>0,增流网络需建立(v,u)边,边权数w ' (v,u)=-w(u,v) =0,即不会再出现负权边。 

    此外,每次迭代计算用(*)式修正一切w(e), 不难证明对每一条x至y的路径而言,其路径长度都同样增加L(x)-L(y)。因此,x至y的最短路径不会因对w(e)的修正而发生变化。

【计算步骤】

1. 对网络G=[V,E,C,W],给出流值为零的初始流。 

2. 作伴随这个流的增流网络G′=[V′,E′,W′]。 

G′的顶点同G:V′=V。 

若G中f(u,v)<c(u,v),则G′中建边(u,v),w(u,v)=w(u,v)。 

若G中f(u,v)>0,则G′中建边(v,u),w′(v,u)=-w(u,v)。 

3. 若G′不存在x至y的路径,则G的流即为最小费用最大流, 

停止计算;否则用标号法找出x至y的最短路径P。 

4. 根据P,在G上增流: 对P的每条边(u,v),若G存在(u,v),则(u,v)增流;若G存在(v,u),则(v,u)减流。增(减)流后,应保证对任一边有c(e)≥ f(e)≥0。 

5. 根据计算最短路径时的各顶点的标号值L(v),按下式修 改G一切边的权数w(e):

  L(u)-L(v)+w(e)→w(e)。  

6. 将新流视为初始流,转2。 

Minimum Cost

Time Limit: 4000MS Memory Limit: 65536K
Total Submissions: 12758 Accepted: 4344
Description

Dearboy, a goods victualer, now comes to a big problem, and he needs your help. In his sale area there are N shopkeepers (marked from 1 to N) which stocks goods from him.Dearboy has M supply places (marked from 1 to M), each provides K different kinds of goods
(marked from 1 to K). Once shopkeepers order goods, Dearboy should arrange which supply place provide how much amount of goods to shopkeepers to cut down the total cost of transport. 

It's known that the cost to transport one unit goods for different kinds from different supply places to different shopkeepers may be different. Given each supply places' storage of K kinds of goods, N shopkeepers' order of K kinds of goods and the cost to
transport goods for different kinds from different supply places to different shopkeepers, you should tell how to arrange the goods supply to minimize the total cost of transport.
Input

The input consists of multiple test cases. The first line of each test case contains three integers N, M, K (0 < N, M, K < 50), which are described above. The next N lines give the shopkeepers' orders, with each line containing K integers (there integers are
belong to [0, 3]), which represents the amount of goods each shopkeeper needs. The next M lines give the supply places' storage, with each line containing K integers (there integers are also belong to [0, 3]), which represents the amount of goods stored in
that supply place. 

Then come K integer matrices (each with the size N * M), the integer (this integer is belong to (0, 100)) at the i-th row, j-th column in the k-th matrix represents the cost to transport one unit of k-th goods from the j-th supply place to the i-th shopkeeper. 

The input is terminated with three "0"s. This test case should not be processed.
Output

For each test case, if Dearboy can satisfy all the needs of all the shopkeepers, print in one line an integer, which is the minimum cost; otherwise just output "-1".
Sample Input
1 3 3
1 1 1
0 1 1
1 2 2
1 0 1
1 2 3
1 1 1
2 1 1

1 1 1
3
2
20

0 0 0

Sample Output
4
-1

#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <stdio.h>
#include <string>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define eps 1e-7
#define M 1000100
//#define LL __int64
#define LL long long
#define INF 0x3f3f3f3f
#define PI 3.1415926535898

const int maxn = 110;
using namespace std;

int c[maxn][maxn];//流量限制;
int f[maxn][maxn];//最大流量限制;
int dis[maxn];//最短路径
int w[maxn][maxn];//费用;
int visit[maxn];//标记;
int path[maxn];//记录路径;
int S,T;//超级汇点与源点;
int need[maxn][maxn], have[maxn][maxn], cost[maxn][maxn][maxn];
int spfa()
{
int i;
queue<int> q;
for(i = 0; i <= T; i++)
{
visit[i] = 0;
path[i] = -1;
dis[i] = INF;
}
dis[S] = 0;
q.push(S);
visit[S] = 1;
while(!q.empty())
{
int t = q.front();
q.pop();
visit[t] = 0;
for(i = 1; i <= T; i++)
{
if(c[t][i] > f[t][i] && dis[i] > dis[t]+w[t][i])
{
path[i] = t;
dis[i] = dis[t]+w[t][i];
if(!visit[i])
{
visit[i] = 1;
q.push(i);
}
}
}
}
if(path[T] == -1)
return 0;
return 1;
}

void find_max_road()
{
while(spfa())//每次spfa求出一条增广路
{
int _max = INF;
int pre = T;
while(path[pre] != -1)
{
_max = min(_max, c[path[pre]][pre]-f[path[pre]][pre]);
pre = path[pre];
}
pre = T;
while(pre != -1)//更新流量
{
f[path[pre]][pre] += _max;
f[pre][path[pre]] = -f[path[pre]][pre];
pre = path[pre];
}
}
}

int main()
{
int i, j, p;
int n, m, k;
while(cin >>n>>m>>k)
{
if(!n && !m && !k)
break;
for(i = 1; i <= n; i++)
for(j = 1; j <= k; j++)
cin >>need[i][j];
for(i = 1; i <= m; i++)
for(j = 1; j <= k; j++)
cin >>have[i][j];
for(i = 1; i <= k; i++)
for(j = 1; j <= n; j++)
for(p = 1; p <= m; p++)
cin >>cost[i][p][j];
S = 0;
T = n+m+1;
int flat = 0;
int sum = 0;
for(i = 1; i <= k; i++)
{
memset(c , 0 , sizeof(c));
memset(f , 0 , sizeof(f));
memset(w , 0 , sizeof(w));
for(j = 1; j <= m; j++)
c[0][j] = have[j][i];
for(j = 1; j <= n; j++)
c[m+j][T] = need[j][i];
for(j = 1; j <= m; j++)
for(p = 1; p <= n; p++)
c[j][p+m] = have[j][i];

for(j = 1; j <= m; j++)
for(p = 1; p <= n; p++)
{
w[j][p+m] = cost[i][j][p];
w[p+m][j] = -w[j][p+m];
}
find_max_road();
for(j = 1; j <= n; j++)
{
if(c[j+m][T] != f[j+m][T])
{
flat = 1;
break;
}
}
if(flat)
break;
for(j = 1; j <= m; j++)
for(p = 1; p <= n; p++)
sum += f[j][p+m]*w[j][p+m];
}
if(flat)
cout<<"-1"<<endl;
else
cout<<sum<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj