(最小费用最大流SPFA+zlw)POJ 2195

Description

On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertically, to an adjacent point. For each little man, you need to pay a $1 travel fee for every step he moves, until he enters a house. The task is complicated with the restriction that each house can accommodate only one little man. 

Your task is to compute the minimum amount of money you need to pay in order to send these n little men into those n different houses. The input is a map of the scenario, a '.' means an empty space, an 'H' represents a house on that point, and am 'm' indicates there is a little man on that point. 


You can think of each point on the grid map as a quite large square, so it can hold n little men at the same time; also, it is okay if a little man steps on a grid with a house without entering that house.

Input

There are one or more test cases in the input. Each case starts with a line giving two integers N and M, where N is the number of rows of the map, and M is the number of columns. The rest of the input will be N lines describing the map. You may assume both N and M are between 2 and 100, inclusive. There will be the same number of 'H's and 'm's on the map; and there will be at most 100 houses. Input will terminate with 0 0 for N and M.

Output

For each test case, output one line with the single integer, which is the minimum amount, in dollars, you need to pay.

Sample Input

2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0

Sample Output

2
10
28

Source

Pacific Northwest 2004

 

题意:每个人都走到一个屋子的最小花费

关键是构图:

                                                  

 

找到两个点作为源点和汇点,在源点和汇点之间有房子和人,假定左边的绿色的为house,右边的蓝色的为people,它们的数量分别为house和people,那么建立连通图。

其中从源点到house之间的边权为0,cap为1、从house到people之间的边权为x,cap为1(其中x为我们需要计算的两点之间的距离)、从people到汇点之间的边权为0,cap为1.

跑最小费用最大流即可。

SPFA费用流

#include<stdio.h>
#include<math.h>
#include<string>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stack>
#include<queue>
#include<map>
#include<vector>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e3+100;
const int maxm=1e6+100;
char mapp[maxn][maxn];
struct Edge{
    int to,next,flow,cost,cap;
}edge[maxm];

struct E{
    int x,y,cost;
}edges1[maxm],edges2[maxm];

int head[maxn],tol;
int pre[maxn],dis[maxn];
bool vis[maxn];//判断是否在队列里面
int N,n,m,house,man;

void debug(){
    cout<<"_____five_____"<<endl;
}

void init()
{
    house=0;
    man=0;
    tol=0;
    memset(head,-1,sizeof(head));
}

void addedge(int u,int v,int cap,int cost){
    edge[tol].to=v,edge[tol].cap=cap,edge[tol].cost=cost,edge[tol].flow=0;
    edge[tol].next=head[u],head[u]=tol++;
    edge[tol].to=u,edge[tol].cap=0,edge[tol].cost=-cost,edge[tol].flow=0;
    edge[tol].next=head[v],head[v]=tol++;
}

bool spfa(int s,int t){
    queue<int>q;
    for(int i=0;i<N;++i){
        dis[i]=inf;
        vis[i]=false;
        pre[i]=-1;
    }
    dis[s]=0,vis[s]=true;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=false;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost)
            {
                dis[v]=dis[u]+edge[i].cost;
                pre[v]=i;
                if(!vis[v]){
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t]==-1) return false;
    return true;
}

int minCostMaxflow(int s,int t,int &cost){
    int flow=0;
    cost=0;
    while(spfa(s,t)){
        int MIN=inf;
        for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
            MIN=min(MIN,edge[i].cap-edge[i].flow);
        }
        for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
            edge[i].flow+=MIN;
            edge[i^1].flow-=MIN;
            cost+=edge[i].cost*MIN;
        }
        flow+=MIN;

    }
    return flow;
}

int main()
{
    while(~scanf("%d%d",&n,&m)&&m+n){
        init();
        for(int i=0;i<n;++i)
            scanf("%s",mapp[i]);
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                if(mapp[i][j]=='H'){
                    edges1[++house].x=i;
                    edges1[house].y=j;
                }
                else if(mapp[i][j]=='m'){
                    edges2[++man].x=i;
                    edges2[man].y=j;
                }
            }
        }
        for(int i=1;i<=man;++i){
            addedge(0,i,1,0);
        }

        for(int i=1;i<=man;++i){
            for(int j=1;j<=house;++j){
                addedge(i,man+j,1,abs(edges1[i].x-edges2[j].x)+abs(edges1[i].y-edges2[j].y));
            }
        }

        for(int i=1;i<=house;++i){
            addedge(man+i,man+house+1,1,0);
        }
        N=house+man+2;
        int cost;
        int flow=minCostMaxflow(0,house+man+1,cost);
        printf("%d\n",cost);
    }
    return 0;
}

因为这道题是二分图类型,使用ZKW费用流为更优选择:

#include<stdio.h>
#include<math.h>
#include<string>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stack>
#include<queue>
#include<map>
#include<vector>
using namespace std;
const int maxn=1e3+10;
const int maxm=maxn*maxn;
const int inf=0x3f3f3f3f;
struct Edge{
    int to,next,cap,flow,cost;
    Edge(int _to = 0 ,int _next = 0,int _cap = 0,int _flow = 0,int _cost = 0):
        to(_to),next(_next),cap(_cap),flow(_flow),cost(_cost){}
}edge[maxm];

struct E_edge{
    int x,y;
}edge_house[maxm],edge_people[maxm];

int N,n,m;
int house,people;
char mapp[maxn];

struct ZKW_MinCostMaxFlow{
    int head[maxn],tot;
    int cur[maxn],dis[maxn];
    bool vis[maxn];
    int ss,tt,N;//源点,汇点和点的总个数
    int min_cost,max_flow;
    void init(){
        house=people=0;
        tot=0;
        memset(head,-1,sizeof(head));
    }
    void addedge(int u,int v,int cap,int cost){
        edge[tot]=Edge(v,head[u],cap,0,cost);
        head[u]=tot++;
        edge[tot]=Edge(u,head[v],0,0,-cost);
        head[v]=tot++;
    }
    int aug(int u,int flow){
        if(u==tt) return flow;
        vis[u]=true;
        for(int i=cur[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(edge[i].cap>edge[i].flow&&!vis[v]&&dis[u]==dis[v]+edge[i].cost){
                int tmp=aug(v,min(flow,edge[i].cap-edge[i].flow));
                edge[i].flow+=tmp;
                edge[i^1].flow-=tmp;
                cur[u]=i;
                if(tmp) return tmp;
            }
        }
        return 0;
    }
    bool modify_label()
    {
        int d=inf;
        for(int u=0;u<N;++u){
            if(vis[u]){
                for(int i=head[u];i!=-1;i=edge[i].next){
                    int v=edge[i].to;
                    if(edge[i].cap>edge[i].flow&&!vis[v]){
                        d=min(d,dis[v]+edge[i].cost-dis[u]);
                    }
                }
            }
        }
        if(d==inf) return false;
        for(int i=0;i<N;++i){
            if(vis[i]){
                vis[i]=false;
                dis[i]+=d;
            }
        }
        return true;

    }
    pair<int,int>mincostmaxflow(int start,int end,int n){
        ss=start,tt=end,N=n;
        min_cost=max_flow=0;
        for(int i=0;i<n;++i) dis[i]=0;
        while(1){
            for(int i=0;i<n;++i){
                cur[i]=head[i];
            }
            while(1){
                for(int i=0;i<n;++i) vis[i]=false;
                int tmp=aug(ss,inf);
                if(tmp==0) break;
                max_flow+=tmp;
                min_cost+=tmp*dis[ss];
            }
            if(!modify_label()) break;
        }
        return make_pair(min_cost,max_flow);
    }
}solve;

int main()
{
    while(~scanf("%d%d",&n,&m)&&n+m){
        solve.init();
        for(int i=0;i<n;++i){
            scanf("%s",mapp);
            for(int j=0;j<m;++j){
                //现在判断的是H
                if(mapp[j]=='H'){
                    edge_house[++house].x=i;
                    edge_house[house].y=j;
                }
            }
            for(int j=0;j<m;++j){
                if(mapp[j]=='m'){
                    edge_people[++people].x=i;
                    edge_people[people].y=j;
                }
            }
        }
        //使用结构体存图
        for(int i=1;i<=house;++i){
            solve.addedge(0,i,1,0);
        }
        for(int i=1;i<=house;++i){
            for(int j=1;j<=people;++j){
                int l=abs(edge_people[i].x-edge_house[j].x)+abs(edge_people[i].y-edge_house[j].y);
                solve.addedge(i,house+j,1,l);
            }
        }
        for(int i=1;i<=people;++i){
            solve.addedge(house+i,house+people+1,1,0);
        }
        N=house+people+2;
        //顶点总数N的确定,是否包含源点
        pair<int,int>ans=solve.mincostmaxflow(0,house+people+1,N);
        printf("%d\n",ans.first);
    }
    return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值