擴點最短路

n1ce2cv發表於2024-09-20

擴點最短路

不把實際位置看作圖上的點,而是把實際位置和該位置的所有狀態的組合看作是圖上的點,BFS 或者 Dijkstra 的過程不變,只是增加了一些點。

864. 獲取所有鑰匙的最短路徑

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;

class Solution {
public:
    int rows, columns;
    // 最多 6 把鑰匙
    int max_k = 6;
    int key = 0;
    vector<int> move{-1, 0, 1, 0, -1};

    bool isCoordinateLegal(int row, int column) {
        return row >= 0 && row < rows && column >= 0 && column < columns;
    }

    // BFS
    int shortestPathAllKeys(vector<string> &grid) {
        rows = grid.size();
        columns = grid[0].size();

        // (x, y, 持有鑰匙的狀態)
        queue<vector<int>> q;
        // 找起點
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < columns; ++j) {
                if (grid[i][j] == '@') {
                    q.emplace(vector<int>{i, j, 0});
                } else if (grid[i][j] >= 'a' && grid[i][j] <= 'f') {
                    // 計算獲得所有鑰匙的最終狀態
                    key |= (1 << (grid[i][j] - 'a'));
                }
            }
        }

        // 表示當前位置的某個狀態是否從佇列中彈出過,狀態是指持有鑰匙的情況
        vector<vector<vector<bool>>> visited(rows, vector<vector<bool>>(columns));
        for (int i = 0; i < rows; ++i)
            for (int j = 0; j < columns; ++j)
                visited[i][j].resize(key, false);

        int step = 1;
        while (!q.empty()) {
            int size = q.size();
            // 逐層彈出
            for (int i = 0; i < size; ++i) {
                auto f = q.front();
                q.pop();
                int x = f[0];
                int y = f[1];
                // 經過(x, y)後,到達下個點前持有鑰匙的狀態
                int s = f[2];
                // 四周
                for (int j = 0; j < 4; ++j) {
                    int nx = x + move[j];
                    int ny = y + move[j + 1];
                    // 越界
                    if (!isCoordinateLegal(nx, ny)) continue;
                    char ch = grid[nx][ny];
                    // 牆
                    if (ch == '#') continue;
                    // 鎖,且沒對應鑰匙
                    if (ch >= 'A' && ch <= 'F' && (s & (1 << (ch - 'A'))) == 0) continue;
                    // 鑰匙,更新持有的鑰匙狀態
                    int ns = s;
                    if (ch >= 'a' && ch <= 'f')
                        ns |= (1 << (ch - 'a'));

                    // 獲得所有鑰匙了
                    if (ns == key) return step;
                    if (visited[nx][ny][ns]) continue;
                    visited[nx][ny][ns] = true;
                    q.emplace(vector<int>{nx, ny, ns});
                }
            }
            step++;
        }

        return -1;
    }
};

LCP 35. 電動車遊城市

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;

class Solution {
public:
    struct cmp {
        bool operator()(vector<int> &v1, vector<int> &v2) {
            return v1[2] > v2[2];
        }
    };

    int electricCarPlan(vector<vector<int>> &paths, int cnt, int start, int end, vector<int> &charge) {
        int n = charge.size();
        vector<vector<pair<int, int>>> graph(n);
        // 無向圖
        for (const auto &item: paths) {
            graph[item[0]].emplace_back(make_pair(item[1], item[2]));
            graph[item[1]].emplace_back(make_pair(item[0], item[2]));
        }

        // (點, 到這個點的剩餘電量, 代價)
        priority_queue<vector<int>, vector<vector<int>>, cmp> heap;
        // vector[i][j] 為到達 i 點時剩餘 j 電量的最小代價
        vector<vector<int>> distance(n, vector<int>(cnt + 1, INT_MAX));
        // 訪問標記
        vector<vector<bool>> visited(n, vector<bool>(cnt + 1, false));

        heap.emplace(vector<int>{start, 0, 0});
        distance[start][0] = 0;

        while (!heap.empty()) {
            auto top = heap.top();
            heap.pop();
            int position = top[0];
            int power = top[1];
            int cost = top[2];
            // 結束
            if (position == end) return cost;
            if (visited[position][power]) continue;
            visited[position][power] = true;
            // 充一格電,到圖中擴出來的點,也就是這個點的其他狀態
            if (power < cnt
                && !visited[position][power + 1]
                && (cost + charge[position] < distance[position][power + 1])) {
                distance[position][power + 1] = cost + charge[position];
                heap.emplace(vector<int>{position, power + 1, distance[position][power + 1]});
            }
            // 不充電,直接去別的點
            for (const auto &item: graph[position]) {
                int nextPosition = item.first;
                int restPower = power - item.second;
                int nextCost = cost + item.second;
                // 到不了下個點,或者下個狀態已經彈出過,就跳過
                if (restPower < 0 || visited[nextPosition][restPower]) continue;
                if (nextCost < distance[nextPosition][restPower]) {
                    distance[nextPosition][restPower] = nextCost;
                    heap.emplace(vector<int>{nextPosition, restPower, nextCost});
                }
            }
        }

        return -1;
    }
};

P4568 [JLOI2011] 飛行路線

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;

int n, m, k, s, t;

vector<int> head;
vector<int> nxt;
vector<int> to;
vector<int> weight;
int cnt;

void build() {
    head.resize(n, 0);
    fill(head.begin(), head.end(), 0);
    nxt.resize((m << 1) + 1);
    to.resize((m << 1) + 1);
    weight.resize((m << 1) + 1);
    cnt = 1;
}

void addEdge(int u, int v, int w) {
    nxt[cnt] = head[u];
    to[cnt] = v;
    weight[cnt] = w;
    head[u] = cnt;
    cnt++;
}

struct cmp {
    bool operator()(vector<int> &v1, vector<int> &v2) {
        return v1[2] > v2[2];
    }
};

int main() {
    cin >> n >> m >> k >> s >> t;
    build();
    for (int i = 0, u, v, w; i < m; ++i) {
        cin >> u >> v >> w;
        addEdge(u, v, w);
        addEdge(v, u, w);
    }

    // distance[i][j] 為到達 i 點,剩餘免費乘坐次數 j 次的最少代價
    vector<vector<int>> distance(n, vector<int>(k + 1, 0x7fffffff));
    // 訪問標記
    vector<vector<bool>> visited(n, vector<bool>(k + 1, false));
    // (點,到點後剩餘的免費次數,最少代價)
    priority_queue<vector<int>, vector<vector<int>>, cmp> heap;

    distance[s][k] = 0;
    heap.emplace(vector<int>{s, k, 0});

    while (!heap.empty()) {
        auto top = heap.top();
        heap.pop();
        int u = top[0];
        int free = top[1];
        int cost = top[2];
        // 結束
        if (u == t) {
            cout << cost;
            return 0;
        }
        if (visited[u][free]) continue;
        visited[u][free] = true;

        for (int ei = head[u]; ei > 0; ei = nxt[ei]) {
            int v = to[ei];
            int w = weight[ei];
            // 使用一張票
            if (free > 0
                && !visited[v][free - 1]
                && cost < distance[v][free - 1]) {
                distance[v][free - 1] = cost;
                heap.emplace(vector<int>{v, free - 1, cost});
            }
            // 不使用
            if (!visited[v][free]
                && cost + w < distance[v][free]) {
                distance[v][free] = cost + w;
                heap.emplace(vector<int>{v, free, cost + w});
            }
        }
    }
}

相關文章