POJ2195 Going Home(最小费用最大流模板题)(附用数组建图和用链式前向星建图代码)

来源:互联网 发布:mac收藏页面快捷键 编辑:程序博客网 时间:2024/06/06 01:59

Going HomeTime Limit:1000MS    Memory Limit:65536KB    64bit IO Format:%lld & %llu

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

Sample Output

21028
这道题他们都说是最小费用最大流的模板题。

题意是:有相等数目的m和H,且该数目不超过100(说是不超过但是dicuss里面很多人吐槽数据有问题,开到200比较妥当),m代表人,H代表家,把每个m匹配一个H,但是每个m匹配H时候的花费是他们之间的曼哈顿距离,我们要保证,这个花费最少,然后输出这个花费。

最小费用最大流,最大流显而易见就是能相连的两个点之间的流为1,最后到超级汇点的流量就是m的数目即可,然后最小费用,就是用最短路去找罢了。SPFA,然后就是我发现我不会用链式前向星,图是建起来了,给出的样例也过了,但是就是wa。遂找了用邻接矩阵写的spfa,然后,我觉得吧,这就是ballman-frod啊,n^2复杂度,跟SPFA还有啥关系吗?不管,数据量很小,就这样放着先吧,希望我能把链式前向星的写法调出来吧。

然后链式前向星的写法我已经写出来了,贴在下面以供参考。确实唉,如果是按照我自己对这个模板的理解,题目的测试数据确实是超出了范围。要开到210才行。

代码如下:

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>#include<queue>using namespace std;const int MAXN = 210;const int INF = 0x7fffffff;struct point{    int x,y;}man[MAXN],home[MAXN];int res[MAXN][MAXN],cost[MAXN][MAXN];int n,k;int dis[MAXN*2],p[MAXN*2];//dis:最短路中的距离数组;p:记录每个点的前驱点bool flag[MAXN*2];//spfa中的记录点是否在队列中bool spfa(int start,int end){    queue<int>q;    int now;    memset(flag,false,sizeof(flag));    memset(p,-1,sizeof(p));    for(int i = start; i <= end; i++)        dis[i] = INF;    q.push(start);    dis[start] = 0;    flag[start] = true;    while(!q.empty())    {        now = q.front();        q.pop();        flag[now] = false;        for(int i = start; i <= end; i++)        {            if(res[now][i] && dis[now] + cost[now][i] < dis[i])            {                dis[i] = dis[now] + cost[now][i];                p[i]=now;                if(!flag[i])                {                    q.push(i);                    flag[i]=true;                }            }        }    }    if(dis[end] == INF)            return false;    return true;}int Min_Cost_Flow(int start, int end){    int u, mn;    int ans_cost = 0;//初始化零流    while(spfa(start, end))    {        u = end;        while(p[u] != -1)        {            res[p[u]][u]-=1;            res[u][p[u]]+=1;            u=p[u];        }        ans_cost += dis[end];//累计费用    }    return ans_cost;}int main(){#ifdef llzhh    freopen("in.txt","r",stdin);#endif // llzhh    int n,m,k1,k2,start,end;    char s[MAXN];    while(scanf("%d%d", &n, &m), n != 0 || m != 0)    {        memset(res,0,sizeof(res));        memset(cost,0,sizeof(cost));        k1 = k2 = 0;        for(int i=0;i<n;i++)        {            scanf("%s",s);            for(int j=0;j<m;j++)            {                if(s[j] == 'm')                {                    man[k1].x=i;                    man[k1].y=j;                    k1++;                }                else if(s[j] == 'H')                {                    home[k2].x=i;                    home[k2].y=j;                    k2++;                }            }        }        start = 0;        end = k1 + k2 + 1;        for(int i=0;i<k1;i++)        {            res[start][i+1]=1;        }        for(int i=0;i<k2;i++)        {            res[k1+i+1][end]=1;        }        for(int i=0;i<k1;i++)            for(int j=0;j<k2;j++)            {                res[i+1][k1+j+1]=1;                cost[i+1][k1+j+1]=abs(man[i].x-home[j].x)+abs(man[i].y-home[j].y);                cost[k1+j+1][i+1]=-cost[i+1][k1+j+1];            }        cout << Min_Cost_Flow(start,end) << endl;    }    return 0;}

好吧,经过多年的努力终于把用链式前向星建的图代入了spfa和MFMC中,然后不断的RE,RE,RE,后来把MAXN改成210就过了。。。果然,还是测试数据有问题啊。

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<map>#include<cmath>#include<vector>#include<queue>using namespace std;const int MAXN = 210;const int MAXM = 5010;const int INT_MAX = 0x7fffffff;struct node{    int to;    int next;    int c;//容量    int f;//费用}edges[MAXN*MAXN];int ppp = 0;int head[2*MAXN];int n,k;int dis[2*MAXN],load[2*MAXM],p[2*MAXN];//dis:最短路中的距离数组;load:记录每个点的前驱边;p:记录每个点的前驱点bool flag[2*MAXN];//spfa中的记录点是否在队列中bool spfa(int start,int end){    queue<int>q;    memset(flag,false,sizeof(flag));    memset(load,-1,sizeof(load));    memset(p,-1,sizeof(p));    for(int i = 0; i < k; ++i)        dis[i] = INT_MAX;    q.push(start);    dis[start] = 0;    flag[start] = true;    while(!q.empty())    {        int e = q.front();        q.pop();        flag[e] = false;        for(int i = head[e]; i != -1; i = edges[i].next)        {            if(edges[i].c)            {                int ne = edges[i].to;                if(dis[ne] - dis[e] > edges[i].f)                {                    dis[ne] = dis[e] + edges[i].f;                    p[ne] = e;//记录前驱点                    load[ne] = i;//记录前驱边                    if(!flag[ne])                    {                        flag[ne] = true;                        q.push(ne);                    }                }            }        }    }    if(dis[end] == INT_MAX)            return false;    return true;}int Min_Cost_Flow(int start, int end){    int u;    int ans_cost = 0;//初始化零流    while(spfa(start, end))    {        u = end;        while(p[u] != -1)        {            edges[load[u]].c -= 1;            edges[load[u]^1].c += 1;            u = p[u];        }        ans_cost += dis[end];//累计费用    }    return ans_cost;}void add_edge(int to,int next,int c,int f){    edges[ppp].to = to;    edges[ppp].next = next;    edges[ppp].c = c;    edges[ppp].f = f;}int main(){#ifdef llzhh    freopen("in.txt","r",stdin);#endif // llzhh    int n,m;    char G[MAXN];    while(scanf("%d%d", &n, &m), n != 0 || m != 0)    {        map <int, int> home, man, all;        map <int, int>::iterator it1, it2;        k = 2;        memset(head,-1,sizeof(head));        for(int i = 0; i < n; i++)        {            scanf("%s", G);            for(int j = 0; j <  m; j++)                if(G[j] == 'H')                {                    home[i * 1000 + j] = k++;                }                else if(G[j] == 'm')                {                    man[i * 1000 + j] = k++;                }        }        ppp = 0;        for(it1 = man.begin(); it1 != man.end(); it1++)        {            add_edge(it1->second,head[0],1,0);            head[0] = ppp++;            add_edge(0,head[it1->second],0,0);            head[it1->second] = ppp++;        }        for(it2 = home.begin(); it2 != home.end(); it2++)        {            add_edge(it2->second,head[1],0,0);            head[1] = ppp++;            add_edge(1,head[it2->second],1,0);            head[it2->second] = ppp++;        }        for(it1 = man.begin(); it1 != man.end(); it1++)        {            for(it2 = home.begin(); it2 != home.end(); it2++)            {                int a = it1->first;                int b = it2->first;                int tf = abs(a / 1000 - b / 1000) + abs(a % 1000 - b % 1000);                add_edge(it2->second,head[it1->second],1,tf);                head[it1->second] = ppp++;                add_edge(it1->second,head[it2->second],0,-tf);                head[it2->second] = ppp++;            }        }        cout << Min_Cost_Flow(0,1) << endl;    }    return 0;}

1 1
原创粉丝点击