算法由来:

在最大流中,我们需要做的只是不停地增广,但是增广全部结束后的残量网络并不唯一,因此不同的增广方式构成的最大流对应了不同的流量。因此,如果给最大流添加一个条件:$\sum{flow*price}$最小,那么可以 增大题目难度缩小解集范围,更有实用价值与普遍适用性。

算法一般步骤:

最小费用最大流的一般步骤是:

与最大流唯一的不同是:增广的方式是用最短路增广:即每次选取所有增广路中最短的

于是模板就是:

/*省略不重要的部分*/

int dist[MX*MX],book[MX*MX],pre[MX*MX],q[MX*MX];
int h,t,now;

bool SPFA(int *flow,int *cost)
{
    memset(dist,0x3f,sizeof(dist));
    memset(book,0,sizeof(book));
    memset(pre,0,sizeof(pre));
    memset(q,0,sizeof(q));
    h=t=1;
    q[h]=S;
    dist[S]=0;
    book[S]=1;
    pre[S]=-1;
    while(h>=t)                                                 //求最短路并记录路径
    {
        now=q[t];
        book[q[t]]=0;
        t++;
        for(int i=fst[now];i!=-1;i=nxt[i])
        {
            if(e[i].cap>0&&dist[e[i].v]>dist[now]+e[i].val)
            {
                dist[e[i].v]=dist[now]+e[i].val;
                pre[e[i].v]=i;
                if(book[e[i].v]==0)
                {
                    book[e[i].v]=1;
                    q[++h]=e[i].v;
                }
            }
        }
    }
    if(dist[T]>=INF)return 0;
    int mxfl=INF,cstd=0;
    now=pre[T];
    while(now!=-1)
    {
        mxfl=min(mxfl,e[now].cap);                              //获得最短增广路的流量
        cstd+=e[now].val;
        now=pre[e[now].u];
    }
    now=pre[T];
    while(now!=-1)
    {
        e[now].cap-=mxfl;                                       //更新残量网络
        e[now^1].cap+=mxfl;
        now=pre[e[now].u];
    }
    *flow+=mxfl;
    *cost+=mxfl*cstd;
    return 1;
}

void mincost()
{
    int flow=0,cost=0;
    while(SPFA(&flow,&cost));
    cout<<cost<<endl;
}

附上 HDU1533 Going Home 的代码,你也可以在这个博客里查看这一题的题解

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

#define MX 200
#define S 0
#define T mnum+hnum+1

using namespace std;

int n,m;
typedef struct eges
{
    int cap,val,u,v;
} edge;
typedef struct points
{
    int x,y;
} point;
edge e[MX*MX];
point man[MX],hos[MX];
int lnum,fst[MX*MX],nxt[MX*MX];
int mnum,hnum;

point makep(int a,int b)
{
    point x;
    x.x=a,x.y=b;
    return x;
}

void init()
{
    lnum=-1;
    mnum=0;
    hnum=0;
    memset(fst,0xff,sizeof(fst));
}

int manhat(point a,point b)
{
    return abs(a.x-b.x)+abs(a.y-b.y);
}

void addeg(int nu,int nv,int cap,int val)
{
    lnum++;
    nxt[lnum]=fst[nu];
    fst[nu]=lnum;
    e[lnum].cap=cap;
    e[lnum].val=val;
    e[lnum].u=nu;
    e[lnum].v=nv;
}

void outeg(int ee)
{
    cout<<e[ee].u<<" "<<e[ee].v<<endl;
}

void input()
{
    char ch;
    scanf("%d%d",&n,&m);
    if(n==0&&m==0)exit(0);
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            ch=getchar();
            while(ch!='H'&&ch!='m'&&ch!='.')ch=getchar();
            if(ch=='m')man[++mnum]=makep(i,j);
            else if(ch=='H')hos[++hnum]=makep(i,j);
        }
    }
}

void build()
{
    for(int i=1; i<=mnum; i++)
    {
        for(int j=1; j<=hnum; j++)
        {
            addeg(i,mnum+j,1,manhat(man[i],hos[j]));
            addeg(mnum+j,i,0,-manhat(man[i],hos[j]));
        }
    }
    for(int i=1; i<=mnum; i++)
    {
        addeg(S,i,1,0);
        addeg(i,S,0,0);
    }
    for(int j=1; j<=hnum; j++)
    {
        addeg(mnum+j,T,1,0);
        addeg(T,mnum+j,0,0);
    }
}

int dist[MX*MX],book[MX*MX],pre[MX*MX],q[MX*MX];
int h,t,now;

bool SPFA(int *flow,int *cost)
{
    memset(dist,0x3f,sizeof(dist));
    memset(book,0,sizeof(book));
    memset(pre,0,sizeof(pre));
    memset(q,0,sizeof(q));
    h=t=1;
    q[h]=S;
    dist[S]=0;
    book[S]=1;
    pre[S]=-1;
    while(h>=t)
    {
        now=q[t];
        book[q[t]]=0;
        t++;
        for(int i=fst[now];i!=-1;i=nxt[i])
        {
            if(e[i].cap>0&&dist[e[i].v]>dist[now]+e[i].val)
            {
                dist[e[i].v]=dist[now]+e[i].val;
                pre[e[i].v]=i;
                if(book[e[i].v]==0)
                {
                    book[e[i].v]=1;
                    q[++h]=e[i].v;
                }
            }
        }
    }
    if(dist[T]>10000)return 0;
    int mxfl=99999,cstd=0;
    now=pre[T];
    while(now!=-1)
    {
        mxfl=min(mxfl,e[now].cap);
        cstd+=e[now].val;
        now=pre[e[now].u];
    }
    now=pre[T];
    while(now!=-1)
    {
        e[now].cap-=mxfl;
        e[now^1].cap+=mxfl;
        now=pre[e[now].u];
    }
    *flow+=mxfl;
    *cost+=mxfl*cstd;
    return 1;
}

void minst()
{
    int flow=0,cost=0;
    while(SPFA(&flow,&cost));
    cout<<cost<<endl;
}

int main()
{
    while(1)
    {
        init();
        input();
        build();
        minst();
    }

    return 0;
}
分类: 文章

1 条评论

konnyakuxzy · 2017年6月17日 11:41 下午

学到了。
好神奇啊,在自己博客里学知识。。。
感谢~
只是您这代码也太长了吧。。。容易吓到萌新(比如我)。。。
170 行。。。

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注