POJ 2195 Going Home【最小费用流 二分图最优匹配】

题目大意:一个n*m的地图,上面有一些人man(m)和数量相等的house(H) 图上的距离为曼哈顿距离 问所有人住进一所房子(当然一个人住一间咯)距离之和最短是多少?

思路:一个人一间房,明显是二分图的模型,边权为人和房子的曼哈顿距离,然后算一下最小距离即可 懒得学KM了 最小费用流的经典建图

 

 

#include <stdio.h>

#include <string.h>

#include <algorithm>

#include <queue>

#include <iostream>

#define maxn 40000

#define inf 0x3f3f3f3f

using namespace std;

struct POINT

{

    int x;

    int y;

}a[maxn],b[maxn];

int head[maxn],root[maxn],point[maxn],next[maxn];

int flow[maxn],cost[maxn],pre[maxn],now=0,dist[maxn];

int n,m,h,h2;

char ch[maxn];

int mabs(int x)

{

    return x>0?x:-x;

}

int distanc(int i,int j)

{

    return mabs(a[i].x-b[j].x)+mabs(a[i].y-b[j].y);

}

void add(int x,int y,int v,int c)

{

    next[++now]=head[x];

    head[x]=now;

    point[now]=y;

    flow[now]=v;

    cost[now]=c;

    root[now]=x;

    next[++now]=head[y];

    head[y]=now;

    point[now]=x;

    flow[now]=0;

    cost[now]=-c;

    root[now]=y;

}

int spfa(int s,int t)

{

    memset(pre,0,sizeof(pre));

    for(int i=0;i<=t;i++)dist[i]=inf;

    dist[s]=0;

    int visit[maxn]={0};

    visit[s]=1;

    queue<int>q;

    q.push(s);

    while(!q.empty())

    {

        int u=q.front();

        q.pop();

        visit[u]=0;

        for(int i=head[u];i;i=next[i])

        {

            int k=point[i];

            if(dist[u]+cost[i]<dist[k] && flow[i]!=0)

            {

                pre[k]=i;

                dist[k]=dist[u]+cost[i];

                if(!visit[k])

                {

                    visit[k]=1;

                    q.push(k);

                }

            }

        }

    }

    return dist[t]!=inf;

}

int main()

{

    while(1)

    {

        scanf("%d%d",&n,&m);

        if(n==0&&m==0)break;

        now=0;

        memset(head,0,sizeof(head));

        int ans=0;

        h=h2=0;

        for(int i=1;i<=n;i++)

        {

            scanf("%s",ch+1);

            for(int j=1;j<=m;j++)

            {

                if(ch[j]=='H')a[++h].x=i,a[h].y=j;

                else if(ch[j]=='m')b[++h2].x=i,b[h2].y=j;

            }

        }

        for(int i=1;i<=h;i++)

        {

            for(int j=1;j<=h;j++)

            {

                int u=distanc(i,j);

                add(j,i+h+1,1,u);

            }

        }

        int s=h*2+10,t=h*2+11;

        for(int i=1;i<=h;i++)add(s,i,1,0);

        for(int i=1;i<=h;i++)add(i+h+1,t,1,0);

        while(spfa(s,t))

        {

            int e=pre[t],minx=flow[e];

            while(e)

            {

                if(flow[e]<minx)minx=flow[e];

                e=pre[root[e]];

            }

            e=pre[t];

            while(e)

            {

                flow[e]-=minx;

                flow[((e-1)^1)+1]+=minx;

                e=[root[e]];

            }

            ans+=dist[t]*minx;

        }

        printf("%d\n",ans);

    }

    return 0;

}

 

转载于:https://www.cnblogs.com/philippica/p/4104461.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值