2011.07.25

        今天发现一个不错的东西,网络流部分的简明教程,用来当指南针好了:http://dev.10086.cn/cmdn/supesite/?uid-1461832-action-viewspace-itemid-6430


最小费用最大流 

    百度的词条今天竟然给力了ORZ,以下为抄袭:http://baike.baidu.com/view/638894.htm

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

        在这一算法中,为了寻求最小费用的增流链,对每一当前流,需建立伴随这一网络流的增流网络。例如图 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。


POJ  2135  Farm Tour

      题意:FJ有N个农场,M条路,FJ要领朋友游玩,从1走到N,再回到1,不走重复路,每条路长度不一样,问最短路长为多少。

     思路: 这个直接看题解用的最小费用最大流的思想解题,把每两个点之间的距离看成1,作为网络中容量的上界(和用最大流计算边联通度有类似)。按照最小费用流建模,若a到b之间有长度为w的路,ab之间费用为w,容量是1。ba之间费用为-w,容量是0。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N=40000;
const int MAXE=20000000;
const int MAXV=1010;
const int inf=1<<30;
int head[N],s,t,cnt,V,E,ans;
int d[N],pre[N];
bool vis[N];
int q[MAXE];
struct Edge
{
    int u,v,c,w,next;
}e[MAXE];
void addedge(int u,int v,int w,int c)
{
    e[cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].c=c;
    e[cnt].next=head[u];
    head[u]=cnt++;
    e[cnt].v=u;
    e[cnt].u=v;
    e[cnt].w=-w;
    e[cnt].c=0;
    e[cnt].next=head[v];
    head[v]=cnt++;
}
bool SPFA(int s)
{
    int l,r;
    memset(pre,-1,sizeof(pre));
    memset(vis,0,sizeof(vis));
    for(int i=0;i<=t;i++) d[i]=inf;
    d[s]=0;
    l=0;r=0;
    q[r++]=s;
    vis[s]=1;
    while(l!=r)               //循环队列
    {
        int u=q[l++];
        if(l>V) l=1;
        vis[u]=0;
        for(int j=head[u];j!=-1;j=e[j].next)
        {
            int v=e[j].v;
            if(e[j].c>0&&d[u]+e[j].w<d[v])
            {
                d[v]=d[u]+e[j].w;
                pre[v]=j;
                if(!vis[v])
                {
                    vis[v]=1;
                    if(r<=V && d[v]<d[q[l+1]]) q[--l]=v;     //SLF优化
                    else   {q[r++]=v;if(r>V) r=1;}
                }
            }
        }
    }
    if(d[t]==inf)
        return 0;
    return 1;
}
void MCMF()
{
    int flow=0;
    while(SPFA(0))
    {
        ans+=d[t];
        int u=t;
        int mini=inf;
        while(u!=s)
        {
            if(e[pre[u]].c<mini)
                mini=e[pre[u]].c;
                u=e[pre[u]].u;
        }
        flow+=mini;
        u=t;
        while(u!=s)
        {
            e[pre[u]].c-=mini;
            e[pre[u]^1].c+=mini;
            u=e[pre[u]].u;
        }
    }
}
int main()
{
    freopen("in","r",stdin);
    freopen("out","w",stdout);
    while(scanf("%d%d",&V,&E)!=EOF)
    {
        s=0;t=++V;cnt=0;
        for(int i=0;i<=t;i++)
            head[i]=-1;
        for(int i=1;i<=E;i++)
        {
            int u,v,cost;
            scanf("%d%d%d",&u,&v,&cost);
            addedge(u,v,cost,1);
            addedge(v,u,cost,1);
        }
        addedge(0,1,0,2);
        addedge(V-1,t,0,2);
        ans=0;
        MCMF();
        printf("%d\n",ans);
    }
    return 0;
}

历史上的今天:我说世界变得太快?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值