POJ 2195 Going Home (网络流之最小费用流)

来源:互联网 发布:网络代理地址设置 编辑:程序博客网 时间:2024/06/05 22:34


Going Home
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 23603 Accepted: 11885

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.mH.5 5HH..m...............mm..H7 8...H.......H.......H....mmmHmmmm...H.......H.......H....0 0

Sample Output

21028

Source

Pacific Northwest 2004


这道题 真心把我给弄得没脾气了,   刚学会 最小费用流,

先讲一下我认知的最小费用流:


最大流(Dinic算法)与最小费用流(SPFA)的区别与共同之处 :
1: 区别: 最大流一般不会用到 u(起点) 费用流需要用到 u(起点)  在遍历寻找短板时从汇点往回找;
2: 区别: 最大流只需要用到cap 容量即可, 而最小费用流 牵扯到费用的问题,所以会有cost 费用问题;
正因为因为费用的问题,最小费用流可以用类似最短路SPFA的算法去找最短增广路;
3: 相似 : 都需要 构建图, 构建源点与汇点, 其实所有的问题都在构建图上;



因为不知道 哪个房子到人是最短的, 所以每个房子都要 与每个人建边,  费用是 x+y 坐标轴距离

然后 房子与源点, 人与汇点建立 边 构成 一个完整的图;



注意! 注意! 注意! 注意! 


我就是死在这上面一晚上,  因为我直接上的头文件,所以开的数组是1e6+5 


然后就一直TLE TLE  一直调啊调 就是 TLE, 没办法, 最后 要了 别人的AC代码,把我的代码 一部分一部分拷贝过去;
AC,AC 依然AC , mmp  最后才想到是不是数组的问题呢,我把数组改成 5e5 然后就神奇的AC了  时间是700ms
我又把数组改成1e5 后 立即变成140ms   这效率 0.0 



代码:  可以拿去做模板 嘿嘿


#include <iostream>#include <stdio.h>#include <algorithm>#include <cmath>#include <ctime>#include <math.h>#include <cstring>#include <string>#include <queue>#include <stack>#include <stdlib.h>#include <list>#include <map>#include <set>#include <bitset>#include <vector>#define mem(a,b) memset(a,b,sizeof(a))#define findx(x) lower_bound(b+1,b+1+bn,x)-b#define FIN      freopen("input.txt","r",stdin)#define FOUT     freopen("output.txt","w",stdout)#define S1(n)    scanf("%d",&n)#define SL1(n)   scanf("%I64d",&n)#define S2(n,m)  scanf("%d%d",&n,&m)#define SL2(n,m)  scanf("%I64d%I64d",&n,&m)#define Pr(n)     printf("%d\n",n)using namespace std;typedef long long ll;const double PI=acos(-1);const int INF=0x3f3f3f3f;const double esp=1e-6;const int maxn=100000;const int MOD=1e9+7;const int mod=1e9+7;int dir[5][2]={0,1,0,-1,1,0,-1,0};struct node{    int u,v,w,c,next;}edge[maxn];struct mp{    int x,y,z;}mpa[maxn],mpb[maxn];int n,m;int head[maxn],start,END,cnt,sum;int dis[maxn],pre[maxn],vis[maxn];void init(){    cnt=0;    mem(head,-1);}int ffabs(int x,int y){    return  fabs(x)+fabs(y);}void add(int u,int v,int c,int w){    edge[cnt].u=u;    edge[cnt].v=v;    edge[cnt].c=c;    edge[cnt].w=w;    edge[cnt].next=head[u];    head[u]=cnt++;    // 有向图, 反射边 cost = -cost    edge[cnt].u=v;    edge[cnt].v=u;    edge[cnt].w=-w;    edge[cnt].c=0;    edge[cnt].next=head[v];    head[v]=cnt++;}int SPFA(){    queue<int>Q;    mem(pre,-1);    mem(vis,0);    mem(dis,INF);    dis[start]=0;    vis[start]=1;    Q.push(start);    while(!Q.empty())    {        int t=Q.front();        Q.pop();        vis[t]=0;        for(int i=head[t];i!=-1;i=edge[i].next)//        {            int t1= edge[i].v;//下一个点            int t2= edge[i].w;// 费用            if(edge[i].c&&dis[t1]>dis[t]+t2)// 最短路            {                dis[t1]=dis[t]+t2;                pre[t1]=i;                if(!vis[t1])                {                    Q.push(t1);                    vis[t1]=1;                }            }        }    }    if(dis[END]==INF)return 0;    return 1;}int MCMF(){    int minflow=0;    int mincost=0;    while(SPFA())    {        minflow=INF;        for(int i=pre[END];i!=-1;i=pre[edge[i].u])        {            minflow=min(minflow,edge[i].c);        }        for(int i=pre[END];i!=-1;i=pre[edge[i].u])        {            edge[i].c-=minflow;            edge[i^1].c+=minflow;        }        mincost+=dis[END];    }    return mincost;}int main(){while(~scanf("%d %d",&n,&m)){    if(n==0&&m==0)            break;        init();        mem(mpa,0);        mem(mpb,0);        char str[120];        start=0;END=n*m+1;        int num=1;        int k=0,h=0;        for(int i=1;i<=n;i++)        {            scanf("%s",str+1);            for(int j=1;j<=strlen(str);j++)            {                if(str[j]=='H')                {                    mpa[++k].x=i;                    mpa[k].y=j;                    mpa[k].z=num;                    add(start,num,1,0);                    num++;                }                if(str[j]=='m')                {                    mpb[++h].x=i;                    mpb[h].y=j;                    mpb[h].z=num;                    add(num,END,1,0);                    num++;                }            }        }        for(int i=1;i<=k;i++)        {            for(int j=1;j<=h;j++)            {                int w=ffabs(mpa[i].x-mpb[j].x,mpa[i].y-mpb[j].y);                add(mpa[i].z,mpb[j].z,1,w);            }        }        int ans=MCMF();        Pr(ans);}return 0;}


123