POJ - 2195 (最小费用最大流)

题意

给你一个矩阵,m表示人,H表示家,这些m想去哪个H就去哪个H,只要一人一个家,总路程最短就行。

思路

模板题,把路程当成花费就可以了。不过顺便解释最小费用最大流吧。其实和普通最大流差不多,也是不断找可行流,更新可行流。不过它的可行流并不是dfs一波随便找了,而是通过SPFA算法找最短路,然后更新最短路上的流量。会了最大流之后没什么难度。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
 
using namespace std;
 
#define mem(a, i) memset(a, i, sizeof a)
 
const int maxn = 100100;
const int inf = 0x3f3f3f3f;
 
int ab(int x)
{
    return x > 0 ? x : -x;
}
 
class MCF
{
private:
    int n, s, t;
    int W[maxn], C[maxn], V[maxn];
    int head[maxn], next[maxn];
    int dis[maxn], prev[maxn], pree[maxn];
    bool vis[maxn];
    int cnt;
    int ans;
public:
    MCF(int nn, int ss, int tt) : n(nn), s(ss), t(tt)
    {
        cnt = -1;
        ans = 0;
        mem(head, 0xff);
        mem(next, 0xff);
    }
    void init(int nn, int ss, int tt)
    {
        n = nn;
        s = ss;
        t = tt;
    }
    void add(int u, int v, int w, int c)
    {
        cnt ++;
        V[cnt] = v;
        W[cnt] = w;
        C[cnt] = c;
        next[cnt] = head[u];
        head[u] = cnt;
    }
    void adde(int u, int v, int w, int c)
    {
        add(u, v, w, c);
        add(v, u, 0, -c);
    }
    bool spfa()
    {
        mem(vis, false);
        mem(prev, 0xff);
        mem(pree, 0xff);
        mem(dis, inf);
        queue <int> q;
        q.push(1);
        dis[1] = 0;
        while(!q.empty())
        {
            int now = q.front();
            q.pop();
            for(int i = head[now]; ~i; i = next[i])
            {
                if(W[i] > 0 && dis[V[i]] > dis[now] + C[i])
                {
                    dis[V[i]] = dis[now] + C[i];
                    prev[V[i]] = now;
                    pree[V[i]] = i;
                    if(!vis[V[i]])
                    {
                        vis[V[i]] = true;
                        q.push(V[i]);
                    }
                }
            }
            vis[now] = false;
        }
        if(dis[t] <= 100000)
        {
            return true;
        }
        return false;
    }
    int min_cost_flow()
    {
        while(spfa())
        {
            int x = t;
            int di = inf;
            while(x != s)
            {
                di = min(di, W[pree[x]]);
                x = prev[x];
            }
            x = t;
            while(x != s)
            {
                W[pree[x]] -= di;
                W[pree[x] ^ 1] += di;
                ans += di * C[pree[x]];
                x = prev[x];
            }
        }
        return ans;
    }
};
 
int main()
{
    char tu[110][110];
    int n, m;
    int mm, hh;
    pair <int, int> M[110], H[110];
    while(cin >> n >> m && (n || m))
    {
        MCF X(2 + 2 * mm, 1, 2 + 2 * mm);
        mm = 0;
        hh = 0;
        for(int i = 1; i <= n; ++ i)
        {
            for(int j = 1; j <= m; ++ j)
            {
                cin >> tu[i][j];
                if(tu[i][j] == 'm')
                {
                    M[++ mm].first = i;
                    M[mm].second = j;
                }
                if(tu[i][j] == 'H')
                {
                    H[++ hh].first = i;
                    H[hh].second = j;
                }
            }
        }
        X.init(2 + 2 * mm, 1, 2 + 2 * mm);
        for(int i = 1; i <= mm; ++ i)
        {
            for(int j = 1; j <= hh; ++ j)
            {
                X.adde(1 + i, 1 + mm + j, 1, ab(M[i].first - H[j].first) + ab(M[i].second - H[j].second));
            }
        }
        for(int i = 1; i <= mm; ++ i)
        {
            X.adde(1, 1 + i, 1, 0);
            X.adde(1 + mm + i, 2 + 2 * mm, 1, 0);
        }
        cout << X.min_cost_flow() << endl;
    }
    return 0;
}
 

转载于:https://www.cnblogs.com/gx1313113/p/10097220.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值