题意:

一个地图上有很多小人要回家,每个小人都可以去任意一栋不同的房子,每个小人去一栋房子的花费为房子和人的曼哈顿距离。求最小支付多少花费可以使小人都回家?

思路:

直接套用最小费用最大流。1. 把每个小人和每个房子连边,容量为 1,费用为距离;2. 把超级源点和每个小人连边,容量为 1,费用为 0;3. 把每个房子和超级汇点连边,容量为 1,费用为 0.

#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 条评论

【题解】 Going Home 最小费用最大流 HDU – 1533 | K-XZY · 2017年6月17日 11:19 下午

[…] abs 大佬太强啦!这题打了 170 行。。。蒟蒻的 3 倍啊 Orz 此题最小费用最大流模板题,也许能用二分图的最大权匹配做。 abs 的题解:http://k-xzy.cf/?p=1440 […]

发表回复

Avatar placeholder

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