HDU1533||ZQUOJ23130Going Home最小费用最大流

题意:

给出NxM的地图,'.'表示可以走的,'H'表示家,'m'表示人,H和m的数目相同

求把所有人移动到H的最小步数

解题思路:  建立超级源点,分别连接每个m,容量为1,费用0

                 建立超级汇点,分别把每个H连接到汇点,容量为1,费用为0

                 再把每个m分别指向H,容量为1,费用为该m到H的横纵左边之差的绝对值的和,|X1-X2|+|Y1-Y2|

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define w 0x3f3f3f3f
using namespace std;
const int r=205;
struct s1{
    int x,y;
}A[r],B[r];
int T,W,pre[r],a[r],b[r],path[r],map[r][r],value[r][r];
struct s2{
    int to,next,f;
}edg[60000];
int fab(int x)
{
    return (x<0)?-x:x;
}
void add(int i,int j,int k)
{
    edg[T].to=j;
    edg[T].f=k;
    edg[T].next=pre[i];
    pre[i]=T++;
    value[i][j]=k;
}
queue<int>q;
int spfa()
{
    memset(path,-1,sizeof(path));
    memset(a,w,sizeof(a));
    memset(b,0,sizeof(b));
    q.push(0);
    a[0]=0;
    b[0]=1;
    int i,j,k,t;
    while(!q.empty())
    {
        t=q.front();
        q.pop();
        b[t]=0;
        for(j=pre[t];j!=-1;j=edg[j].next)
        {
            k=edg[j].to;
            if(map[t][k]>0&&(a[k]==w||a[k]>a[t]+edg[j].f))
            {
                a[k]=a[t]+edg[j].f;
                path[k]=t;
                if(!b[k])
                {
                    b[k]=1;
                    q.push(k);
                }
            }
        }
    }

    return a[W]==w?0:1;
}

int EK()
{
    int i,j,k=w,sum=0;
    for(i=W;i!=-1;i=path[i])
    {
        if(path[i]!=-1)
            k=min(k,map[path[i]][i]);
    }
    for(i=W;i!=-1;i=path[i])
    {
        if(path[i]!=-1)
        {
            map[path[i]][i]-=k;
            map[i][path[i]]+=k;
            sum+=value[path[i]][i];
        }
    }
    return sum;
}

int slove()
{
    int m=0;
    while(spfa())
        m+=EK();
    return m;
}
int main()
{
    int n,m,i,j,k,t;
    char str[r][r];
    while(~scanf("%d%d",&n,&m)&&n+m)
    {
        memset(pre,-1,sizeof(pre));
        memset(map,0,sizeof(map));
        memset(value,0,sizeof(value));
        T=0,t=0,k=0;
        for(i=0;i<n;i++)
        {
            scanf("%s",str[i]);
            for(j=0;j<m;j++)
            {
                if(str[i][j]=='H')
                {
                    A[t].x=i;
                    A[t++].y=j;
                }
                if(str[i][j]=='m')
                {
                    B[k].x=i;
                    B[k++].y=j;
                }
            }
        }
        W=t+t+1;
        for(i=1;i<=t;i++)
        {
            add(0,i,0);
            map[0][i]=1;
            add(i,0,0);
            for(j=1;j<=t;j++)
            {
                k=fab(A[i-1].x-B[j-1].x)+fab(A[i-1].y-B[j-1].y);
                add(i,j+t,k);
                map[i][j+t]=1;
                add(j+t,i,-k);
            }
            add(i+t,W,0);
            map[i+t][W]=1;
            add(W,i+t,0);
        }
        printf("%d\n",slove());
    }
return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值