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

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

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
这道题他们都说是最小费用最大流的模板题。

题意是:有相等数目的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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值