原文地址:
http://hi.baidu.com/blogofwangmeng/home
网络流 之 最小费用流:
最小费用流问题是求流尽量大时的最小费用。通常用两种算法:消圈算法 和 最小费用路算法。
g[][]记录每边的容量,cost[][]记录每边的单位费用,f[][]记录当前流,pre[]记录增广路中每个点的前驱,d[]记录每个点的权,n为顶点数。
消圈算法:
定理:网络G的最大流flow是G的一个最小费用流的充分必要条件是,flow所对应的残留网络中没有负费用圈。
其中,求一个网络中的负费用圈可以使用Bellman-Fork算法,将每边的费用作为该边的权值。
基本步骤:
1、 用最大流算法构造最大流flow;
2、 如果残留网络中不存在负费用圈,则算法结束。否则进行步骤3;
3、 沿找到的负费用圈增流,转到步骤2.
具体代码如下:
void lowestcost(int s,int e)
{
memset(f,0,sizeof(f));
maxflow(s,e);
int u;
while(u=Bellman_Ford(s,e),u!=-1)
{
int min=MAXINT;
int k=u;
do{
min=g[pre[k]][k]<min? g[pre[k]][k] : min;
if (g[k][pre[k]]!=0)
min=g[k][pre[k]]<min? g[k][pre[k]] : min;
k=pre[k];
}while(k!=u);
k=u;
do{
f[pre[k]][k]+=min;
g[pre[k]][k]-=min;
if (f[k][pre[k]]!=0)
{
f[k][pre[k]]-=min;
g[k][pre[k]]+=min;
}
k=pre[k];
}while(k!=u);
}
}
int Bellman_Ford(int s,int e)
{
memset(pre,-1,sizeof(pre));
memset(d,MAXINT,sizeof(d));
d[s]=0;
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
if (g[j][i]!=0&&d[i]>d[j]+f[j][i]*cost[j][i])
{
d[i]=d[j]+f[j][i]*cost[j][i];
pre[i]=j;
}
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
if (g[i][j]!=0&&d[j]>d[i]+f[i][j]*cost[i][j])
return j;
return -1;
}
最小费用路算法:
通过不断地在残留网络中寻找从源s到汇e的最小费用路,然后沿最小费用路增流,直到找不到最小费用路。其中从s到e的最小费用路是从s到e的以费用为权的最短路。
具体代码如下:
void lowestcost(int s,int e)
{
memset(f,0,sizeof(f));
shortest();
while(pre[e]!=-1)
{
int min=MAXINT;
int k=e;
while(k!=s)
{
min=g[pre[k]][k]<min? g[pre[k]][k] : min;
if (g[k][pre[k]]!=0)
min=g[k][pre[k]]<min? g[k][pre[k]] : min;
k=pre[k];
}
k=e;
while(k!=s)
{
f[pre[k]][k]+=min;
g[pre[k]][k]-=min;
if (f[k][pre[k]]!=0)
{
f[k][pre[k]]-=min;
g[k][pre[k]]+=min;
}
k=pre[k];
}
shortest();
}
}
void shortest(int s,int e)
{
memset(pre,-1,sizeof(pre));
memset(d,MAXINT,sizeof(d));
d[s]=0;
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
if (g[j][i]!=0&&d[i]>d[j]+f[j][i]*cost[j][i])
{
d[i]=d[j]+f[j][i]*cost[j][i];
pre[i]=j;
}
}