【网络流】hdu1533 Going Home 费用流

题意:地图中有n个小人和n个小房子,每个小人和每个房子之间都有一定的距离,现在要使每个房子都住进一个小人,求这n个小人走进各自房子的距离之和的最小值。
难度:1
题解:二分图最大权匹配。用最小费用最大流求解。设立一个附加源和附加汇,附加源和每个小人连一条流量为1,费用为0的边,每个房子向附加汇连一条流量为1费用为0的边,每个小人向每个房子连一条流量不为0,费用为彼此距离的边,求最小费用最大流。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define inf (1<<29)
const int maxn = 222 , maxm = 55555;
struct  Edge {
    int u,v,f,c,next;
}edge[maxm];
int E,head[maxn];
void init() {
    E=0;memset(head,-1,sizeof(head));
}
void _add(int u,int v,int f,int c) {
    edge[E].u=u;edge[E].v=v;edge[E].f=f;edge[E].c=c;edge[E].next=head[u];head[u]=E++;
}
void addedge(int u,int v,int f,int c) {
    _add(u,v,f,c); _add(v,u,0,-c);
}
int n , maxf , minc;
int dist[maxn] , p[maxn] , sta[maxn];
bool vis[maxn];
bool spfa(int s,int t) {
    int u , v ,f , c;
    for(int i=0;i<n;i++) dist[i] = inf , vis[i] = 0 , p[i] = -1;
    int top = 0;
    sta[++top] = s;
    dist[s] = 0;
    while(top) {
        u=sta[top--];
        vis[u] = 0;
        for(int i=head[u];i!=-1;i=edge[i].next) {
            v = edge[i].v;
            f = edge[i].f;
            c = edge[i].c;
            if(f && dist[v] > dist[u] + c) {
                p[v] = i;
                dist[v] = dist[u] + c;
                if(!vis[v]) {
                    vis[v] = 1;
                    sta[++top] = v;
                }
            }
        }
    }
    return dist[t]  != inf;
}
void mcmf(int s,int t) {
    maxf = minc = 0;
    while(spfa(s,t)) {
        //printf("tmp of spfa : %d\n",dist[t]);
        int minf = inf;
        for(int i=p[t];i!=-1;i=p[edge[i].u])
            if(edge[i].f < minf) minf = edge[i].f;
        maxf += minf;
        minc += minf * dist[t];
        for(int i=p[t];i!=-1;i=p[edge[i].u]) {
            edge[i].f -= minf;
            edge[i^1].f += minf;
        }
    }
}
char map[maxn][maxn];
int N , M , n1 , n2;
int x[maxn] , y[maxn];
int xx[maxn] , yy[maxn];
int dis(int i,int j) {
    return abs(x[i]-xx[j]) + abs(y[i]-yy[j]);
}
int main() {
    while(~scanf("%d%d",&N,&M) && N+M) {
        init();
        for(int i=0;i<N;i++) scanf("%s",map[i]);
        n1 = n2 = 0;
        for(int i=0;i<N;i++)
            for(int j=0;j<M;j++) {
                if(map[i][j] == 'H') {
                    x[n1] = i;
                    y[n1] = j;
                    n1 ++;
                }
                if(map[i][j] == 'm') {
                    xx[n2] = i;
                    yy[n2] = j;
                    n2 ++;
                }
            }
        //printf("n1 == %d\n n2 == %d\n",n1,n2);
        int s = n1<<1 , t = n1<<1|1;
        n = t + 1;
        for(int i=0;i<n1;i++)
            for(int j=0;j<n2;j++)
                addedge(i,j+n1,1,dis(i,j));
        for(int i=0;i<n1;i++) {
            addedge(s,i,1,0);
            addedge(i+n1,t,1,0);
        }
        mcmf(s,t);
        printf("%d\n",minc);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值