HDU_1533_Going Home(最小费用流模板)

Going Home

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3492    Accepted Submission(s): 1795



Problem 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
 
题意:N*M的地图上有等数量的人和房子,每个房子只能容纳一个人,人可以上下左右移动,每移动一格需要花费1美元,问所有人和房子匹配好的最小费用是多少。
分析:最小费用流。关键在于建图。假设有n个人,首先加入超级源点s(0)、超级汇点t(2*n+1),源点s指向所有的人,容量为1,费用为0;所有的房子指向汇点t,容量为1,费用为0;每一个人都指向所有的房子,容量为1,费用为曼哈顿距离(|x1-x2| + |y1-y2|)。接着就是跑最小费用流的模板了!
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1533
代码清单:
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<cctype>
#include<string>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;

#define end() return 0

typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;

const int maxn = 300 + 5;
const int INF = 0x7f7f7f7f;

struct Edge{
    int from,to,cap,flow,cost;
    Edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){}
};

struct MCMF{
    int n,m;
    vector<Edge>edge; //边数的两倍
    vector<int>G[maxn]; //邻接表,G[i][j]表示i的第j条边在e数组中的序号
    int inq[maxn]; //是否在队列
    int d[maxn]; //Bellman-Ford
    int p[maxn]; //上一条弧
    int a[maxn]; //可改进量

    void init(int n){
        this -> n = n;
        for(int i=0;i<=n;i++) G[i].clear();
        edge.clear();
    }

    void addEdge(int from,int to,int cap,int cost){
        edge.push_back(Edge(from,to,cap,0,cost));
        edge.push_back(Edge(to,from,0,0,-cost));
        m=edge.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool BellmanFord(int s,int t,int& flow,int& cost){
        memset(d,INF,sizeof(d));
        memset(inq,0,sizeof(inq));
        d[s]=0; inq[s]=1; p[s]=0; a[s]=INF;

        queue<int>q;
        q.push(s);
        while(!q.empty()){
            int u=q.front();q.pop();
            inq[u]=0;
            for(int i=0;i<G[u].size();i++){
                Edge& e=edge[G[u][i]];
                if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to]){
                        q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }

        if(d[t]==INF) return false;
        flow+=a[t];
        cost+=d[t]*a[t];
        for(int u=t;u!=s;u=edge[p[u]].from){
            edge[p[u]].flow+=a[t];
            edge[p[u]^1].flow-=a[t];
        }
        return true;
    }

    //需要保证初始网络中没有负权圈
    int MincostMaxflow(int s,int t){
        int flow=0,cost=0;
        while(BellmanFord(s,t,flow,cost));
        return cost;
    }
};

struct Node{
    int x,y;
    Node(){}
    Node(int _x,int _y):x(_x),y(_y){}
};

int N,M,ma,ho;
char str[105][105];
Node man[105],house[105];
MCMF mcmf;

void input(){
    ma=ho=0;
    for(int i=0;i<N;i++){
        scanf("%s",str[i]);
        for(int j=0;j<M;j++){
            if(str[i][j]=='m'){
                man[ma++]=Node(i,j);
            }
            if(str[i][j]=='H'){
                house[ho++]=Node(i,j);
            }
        }
    }
}

int getDis(Node a,Node b){
    return abs(a.x-b.x)+abs(a.y-b.y);
}

void createGraph(){
    N=ma+ho+2;
    mcmf.init(N);
    for(int i=0;i<ma;i++)
        mcmf.addEdge(0,i+1,1,0);
    for(int i=0;i<ho;i++)
        mcmf.addEdge(ma+i+1,N-1,1,0);
    for(int i=0;i<ma;i++){
        for(int j=0;j<ho;j++){
            mcmf.addEdge(i+1,ma+j+1,1,getDis(man[i],house[j]));
        }
    }
}

void solve(){
    createGraph();
    printf("%d\n",mcmf.MincostMaxflow(0,N-1));
}

int main(){
    while(scanf("%d%d",&N,&M)!=EOF){
        if(!N&&!M) break;
        input();
        solve();
    }
    end();
}



你可能感兴趣的:(Algorithm,ACM,HDU,maxflow,mincost)