Codeforces 1754F Dijkstra

文章是关于Codeforces上题号为1754F的题目TheBeach的解答。作者通过建立图模型,利用Dijkstra算法求解从空位出发,经过多次移动到达目标位置的最小花费。关键在于理解浴床只能向相邻奇偶性相同的位置移动,以及如何构建图和优化时间复杂度至O(mnlog(mn))。
摘要由CSDN通过智能技术生成
题意

传送门 Codeforces 1754F The Beach

题解

观察到一张浴床至多移动一次,否则可以构造出花费更小的方案。将浴床的移动,看作将一个空位 ‘.’ 移动到了之前浴床的某一半所占据的格子上。方案的构造可能涉及多个浴床的移动。考虑建图,求解从所有空位 ‘.’ 出发,经过不断地移动抵达某个位置的花费。

对于图中相邻格子,两者横纵坐标求和的奇偶性不同。位于某一个位置的空位 ‘.’ 只能移动到与其奇偶性相同的位置上。 那么枚举相邻格子,答案为空位 ‘.’ 抵达两者的花费之和。总时间复杂度 O ( m n log ⁡ ( m n ) ) O\big( mn\log(mn)\big) O(mnlog(mn))

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using P = pair<ll, int>;
struct Edge {
    int to, cost;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    int p, q;
    cin >> p >> q;

    vector<string> mat(n);
    for (int i = 0; i < n; ++i) {
        cin >> mat[i];
    }

    auto judge = [&](int x, int y) {
        return 0 <= x && x < n && 0 <= y && y < m;
    };

    vector<vector<Edge>> g(n * m);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            auto c = mat[i][j];
            if (c != 'L' && c != 'U') {
                continue;
            }
            set<pair<int, int>> st;
            if (c == 'L') {
                st.insert({i, j});
                st.insert({{i, j + 1}});
            } else {
                st.insert({i, j});
                st.insert({i + 1, j});
            }

            for (auto [x, y] : st) {
                for (int dx = -1; dx <= 1; ++dx) {
                    for (int dy = -1; dy <= 1; ++dy) {
                        if (abs(dx) + abs(dy) > 1) {
                            continue;
                        }
                        int nx = x + dx, ny = y + dy;
                        if (!judge(nx, ny)) {
                            continue;
                        }
                        if (st.count({nx, ny})) {
                            continue;
                        }
                        if (mat[nx][ny] == '#') {
                            continue;
                        }
                        for (auto [x2, y2] : st) {
                            if (abs(x2 - nx) + abs(y2 - ny) == 1) {
                                continue;
                            }
                            int c = nx == x2 || ny == y2 ? q : p;
                            g[nx * m + ny].push_back({x2 * m + y2, c});
                        }
                    }
                }
            }
        }
    }

    vector<ll> ds(n * m, 1e18);
    priority_queue<P, vector<P>, greater<P>> que;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (mat[i][j] == '.') {
                que.push({0ll, i * m + j});
                ds[i * m + j] = 0;
            }
        }
    }

    vector<int> upd(n * m);
    while (!que.empty()) {
        auto [d, v] = que.top();
        que.pop();
        if (upd[v]) {
            continue;
        }
        upd[v] = 1;
        for (auto [to, cost] : g[v]) {
            auto d2 = d + cost;
            if (ds[to] > d2) {
                ds[to] = d2;
                que.push({d2, to});
            }
        }
    }

    ll res = 1e18;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (i + 1 < n) {
                res = min(res, ds[i * m + j] + ds[(i + 1) * m + j]);
            }
            if (j + 1 < m) {
                res = min(res, ds[i * m + j] + ds[i * m + (j + 1)]);
            }
        }
    }

    cout << (res == 1e18 ? -1 : res) << '\n';

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值