poj2195(最小费用流)

(最小费用流的入门基础题)
题目大概意思为在一个矩阵中,有n个小人和n个房子,小人每走一格花费1美元,小人只能上下左右走,且可以经过房子但不进去,为了让每个小人有自己的房子,求最小费用

首先统计每个小人去每个房子分别最少需要多少钱, 小人到房子所需费用等于两者的横坐标差的绝对值加上纵坐标差的绝对值。然后把小人与房子之间连上一条容量为1,费用为到达所需费用的值

然后套最小费用流模板即可

(其他细节我就注释在代码中)

#include <iostream>
#include <stdio.h>
#include <utility>
#include <stdlib.h>
#include <vector>
#define INF 1000000005
using namespace std;
vector<pair<int, int> > flag1;     //用于放房子的位置
vector<pair<int, int> > flag2;	   //用于放小人的位置
int N, M, n, data[105][105];
struct edge
{
    int to, cap, coust, rev;
    edge(int t, int c, int s, int r)
    {
        to = t; cap = c; coust = s; rev = r;
    }
};
vector<struct edge> ddd[205];
int dist[205];
int prev[205];
int prep[205];
char ch;
void add_edge(int i, int j, int k)
{
    ddd[i].push_back(edge(j, 1, k, ddd[j].size()));
    ddd[j].push_back(edge(i, 0, -k, ddd[i].size() - 1));
}
int min_coust(int from, int to, int flow)
{
    int money = 0;
    while(flow--)
    {
        fill(dist, dist + 2 * n + 2, INF);
        dist[from] = 0;
        bool flag = true;
        while(flag)
        {
            flag = false;
            for(int i = 0; i <= 2 * n + 1; i++)
            {
                if(dist[i] != INF)
                {
                    for(int j = 0; j < ddd[i].size(); j++)
                    {
                        struct edge e = ddd[i][j];
                        if(e.cap != 0 && dist[i] + e.coust < dist[e.to])
                        {
                            flag = true;
                            dist[e.to] = dist[i] + e.coust;
                            prev[e.to] = i;
                            prep[e.to] = j;
                        }
                    }
                }
            }
        }
        for(int i = to; i != from; i = prev[i])
        {
            ddd[prev[i]][prep[i]].cap--;
            ddd[i][ddd[prev[i]][prep[i]].rev].cap++;
        }
        money = money + dist[to];
    }
    return money;
}
int main()
{
    while(1)
    {
        flag1.clear();
        flag2.clear();
        for(int i = 0; i < 205; i++)
        {
            ddd[i].clear();
        }
        scanf("%d %d", &N, &M);
        if(N == 0 && M == 0)
        {
            break;
        }
        for(int i = 0; i < N; i++)
        {
            getchar();
            for(int j = 0; j < M; j++)
            {
                scanf("%c", &ch);
                if(ch == 'H')
                {
                    data[i][j] = 2;
                    flag1.push_back(make_pair(i, j));
                }
                else if(ch == 'm')
                {
                    data[i][j] = 1;
                    flag2.push_back(make_pair(i, j));
                }
                else
                {
                    data[i][j] = 0;
                }
            }
        }
        n = flag1.size();
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < n; j++)
            {
                int num = abs(flag1[i].first - flag2[j].first) + abs(flag1[i].second - flag2[j].second);
                add_edge(i + 1, j + n + 1, num);
            }
            add_edge(0, i + 1, 0);
            add_edge(i + n + 1, 2 * n + 1, 0);
        }
        printf("%d\n", min_coust(0, 2 * n + 1, n));
    }
}

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值