杨柳

本文深入探讨了最小费用最大流算法在解决特定棋盘游戏中寻找最短路径问题的应用。通过构建复杂网络模型,详细解释了如何从源点到目标点进行费用流计算,以达到所有棋子从初始位置到指定目标的最优解。文章提供了完整的算法实现代码,并讨论了在实际运行中可能遇到的性能瓶颈。

题目

传送门

题意

给出一个\(r * c\)的矩阵, 有\(n\)颗棋子, 给出棋子的初始位置, 有\(n\)个目标点, 有些位置不能走, 位于\((x, y)\)的棋子, 可以一步走到\((x + a, y + b)\), \((x - a, y + b)\), \((x + a, y - b)\), \((x - a, y + b)\), \((x + b, y + a)\), \((x + b, y - a)\), \((x - b, y + a)\). \((x - b, y - a)\), 求每个棋子都走到目标点的最短时间。

题解

由源点向棋子的初始位置连容量为\(1\)费用为\(0\)的边
由目标位置向汇点连容量为\(1\)用为\(0\)的边
对于每一个能走的点, 向他能够一步走到的点连容量为\(\infty\)费用为\(1\)的边
然后跑费用流

此题卡常, 如果用MCMF的话要多路增广才能过。

代码

#include <iostream>
#include <cstdlib>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;


const int N = 10010, M = 160010;

const int INF = 0x2A2A2A2A;


struct edge
{
    int from, to, flow, cap, dis;
    edge() { }
    edge(int _1, int _2, int _3, int _4, int _5) : from(_1), to(_2), flow(_3), cap(_4), dis(_5) { }
};

struct MCMF
{
    int head[N], nxt[M], tot;
    edge edges[M];

    inline void init()
    {
        memset(head, -1, sizeof(head));
        tot = 0;
    }
    
    inline void add_edge(int x, int y, int z, int k)
    {
        edges[tot] = edge(x, y, 0, z, k);
        nxt[tot] = head[x];
        head[x] = tot++;
        edges[tot] = edge(y, x, 0, 0, -k);
        nxt[tot] = head[y];
        head[y] = tot++;
    }

    int s, t;

    int L, R;

    int cost;
    int flow;

    int dist[N];
    bool inq[N];

    bool SPFA()
    {
        memset(dist + L, 127 / 3, sizeof(int) * (R - L + 1));
        memset(inq + L, 0, sizeof(int) * (R - L + 1));
        
        queue<int> q;
        q.push(s);
        dist[s] = 0;
        inq[s] = 1;
        
        while (!q.empty())
        {
            int x = q.front(); q.pop();
            inq[x] = 0;
            cur[x] = head[x];
            for (register int i = head[x]; ~i; i = nxt[i])
            {
                edge & e = edges[i];
                if (e.flow < e.cap && dist[e.to] > dist[x] + e.dis)
                {
                    dist[e.to] = dist[x] + e.dis;
                    if (!inq[e.to])
                        inq[e.to] = 1, q.push(e.to);
                }
            }
        }
        
        return dist[t] != INF;
    }
    
    bool vis[N];
    
    int cur[N];
    
    int dfs(int x, int a)
    {
        if (x == t || a == 0) return a;
        
        vis[x] = 1;
        
        int flow = 0, f;
        
        for (int & i = cur[x]; ~i; i = nxt[i])
        {
            edge & e = edges[i];
            if (vis[e.to]) continue;
            if (dist[e.to] == dist[x] + e.dis && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0)
            {
                e.flow += f;
                edges[i^1].flow -= f;
                cost += e.dis * f;
                flow += f;
                a -= f;
                if (a == 0) break;
            }
        }
        
        vis[x] = 0;
        
        return flow;
    }

    pair<int, int> mincostmaxflow(int _s, int _t)
    {
        cost = 0;
        flow = 0;
        s = _s, t = _t;
        while (SPFA())
            flow += dfs(s, INF);
        return make_pair(flow, cost);
    }
} solve;


char mp[110][110];

int n, m;
int k, A, B;


inline int getid(int x, int y) { return (x-1) * m + y; }


int main()
{
    scanf("%d %d %d %d %d", &n, &m, &k, &A, &B);
    
    int S = n * m + 1, T = n * m + 2;
    
    solve.init();
    solve.L = 0, solve.R = n * m + 2;
    
    for (register int i = 1; i <= n; i++)
        scanf("%s", mp[i] + 1);
    
    for (register int i = 1; i <= k; i++)
    {
        int x, y;
        scanf("%d %d", &x, &y);
        solve.add_edge(getid(x, y), T, 1, 0);
    }
    
    for (register int i = 1; i <= k; i++)
    {
        int x, y;
        scanf("%d %d", &x, &y);
        solve.add_edge(S, getid(x, y), 1, 0);
    }
    
    for (register int x = 1; x <= n; x++)
        for (register int y = 1; y <= m; y++)
            if (mp[x][y] == '.')
            {
                if (x + A <= n && y + B <= m && mp[x + A][y + B] == '.')
                {   solve.add_edge(getid(x, y), getid(x + A, y + B), INF, 1);
                    solve.add_edge(getid(x + A, y + B), getid(x, y), INF, 1);
                }
                if (x + A <= n && y - B >= 1 && mp[x + A][y - B] == '.')
                {   solve.add_edge(getid(x, y), getid(x + A, y - B), INF, 1);
                    solve.add_edge(getid(x + A, y - B), getid(x, y), INF, 1);
                }
                if (x + B <= n && y + A <= m && mp[x + B][y + A] == '.')
                {   solve.add_edge(getid(x, y), getid(x + B, y + A), INF, 1);
                    solve.add_edge(getid(x + B, y + A), getid(x, y), INF, 1);
                }
                if (x + B <= n && y - A >= 1 && mp[x + B][y - A] == '.')
                {   solve.add_edge(getid(x, y), getid(x + B, y - A), INF, 1);
                    solve.add_edge(getid(x + B, y - A), getid(x, y), INF, 1);
                }
            }
    
    pair<int, int> Ans = solve.mincostmaxflow(S, T);
    
    if (Ans.first == k) printf("%d\n", Ans.second);
    else puts("-1");
    
    return 0;
}

转载于:https://www.cnblogs.com/2016gdgzoi509/p/10022588.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值