网络流

原文地址:

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;
                            }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值