1. 程式人生 > 實用技巧 >從格點最短路模型談起

從格點最短路模型談起

從格點最短路模型談起

模型

給定 \(n\times n\) 的格點,允許向上下左右四個方向走,要求從 \((0,\ 0)\)\((n - 1,\ n - 1)\) 的最短距離

\(DP\) 的困境

對於當前點 \((i,\ j)\),可以由四個方向轉移進來,然而在進行狀態轉移時,下面的位置 \(dp[i + 1,\ j]\) 還未做好決策,因此不滿足無後效性,所以 \(dp\) 無法解決此模型

\(Uniform\ cost\ search\)

考慮用 \(bfs\) 結合 \(priority\ queue\) 實現,即 \(UCS\) 演算法

注意,節點 \(i\) 只有在出隊時,即離開 \(close\)

表時,才標記其為已訪問,即加入 \(open\)

class Solution1 // uniform cost search
{
public:
    bool check(int x, int y, int n) {return x >= 0 && x < n && y >= 0 && y < n;}
    int uniformCostSearch(int n, vector<vector<int>>& cost)
    {
        int vis[n][n];
        memset(vis, 0, sizeof(vis));
        priority_queue<node> pq;
        pq.push(node(0, 0, cost[0][0]));
        while(!pq.empty()) {
            node temp = pq.top(); pq.pop();
            if(temp.x == n - 1 && temp.y == n - 1) return temp.w;
            if(!vis[temp.x][temp.y]) {
                vis[temp.x][temp.y] = 1; // marked when leaving closed set
                for(int k = 0; k < 4; ++k) {
                    int tx = temp.x + DX[k], ty = temp.y + DY[k];
                    if(!vis[tx][ty] && check(tx, ty, n))
                        pq.push(node(tx, ty, temp.w + cost[tx][ty]));
                }
            }
        }
        return 0;
    }
    int exe(int n, vector<vector<int>>& cost)
    {
        return uniformCostSearch(n, cost);
    }
};

圖論建模後 \(dijkstra\)

考慮圖論建模,對於每一個點 \((i,\ j)\),可以與 \((i - 1,\ j),\ (i + 1,\ j),\ (i,\ j - 1),\ (i,\ j + 1)\) 連邊,邊權為 \(cost[i - 1,\ j],\ cost[i + 1,\ j],\ cost[i,\ j - 1],\ cost[i,\ j + 1]\),並且將 \((i,\ j)\) 對映為 \(i\times n + j\),隨後在新的圖上跑 \(dijkstra\),最後統計答案時需要加上 \(cost[0,\ 0]\)

為了列印路徑,在每次決策時,用 \(pre\) 陣列記錄前繼 \((predecessor)\)

,逆序列印即可

class Solution2 // graph modeling + Dijkstra
{
public:
    Solution2()
    {
        memset(pre, 0, sizeof(pre));
        memset(dis, inf, sizeof(dis));
        memset(vis, 0, sizeof(vis));
        mp.clear();
    }
    bool check(int x, int y, int n) {return x >= 0 && x < n && y >= 0 && y < n;}
    void graphModeling(int n, vector<vector<int>>& cost)
    {
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < n; ++j) {
                mp[i * n + j] = pii {i, j};
                for(int k = 0; k < 4; ++k) {
                    int tx = i + DX[k], ty = j + DY[k];
                    if(check(tx, ty, n)) g[i * n + j].pb(pii {tx * n + ty, cost[tx][ty]});
                }
            }
        }
    }
    void printNewGraph(int n)
    {
        /*Print the new graph*/
        cout << "The new graph is as follows" << endl;
        for(int i = 0; i < n * n; ++i) {
            cout << i << ": ";
            for(auto j: g[i]) cout << "(" << j.fi << ", " << j.se << ") ";
            puts("");
        }
        puts("");
    }
    int dijkstra(int s, int t, int n, int cost)
    {
        dis[s] = 0; vis[s] = 1;
        for(int i = 0; i < n * n; ++i){
            int mini = inf, u = 0;
            for(int j = 0; j < n * n; ++j)
                if(!vis[j] && dis[j] < mini) mini = dis[j], u = j;
            vis[u] = 1;
            for(auto i: g[u]){
                if(!vis[i.fi] && dis[u] + i.se < dis[i.fi]) {
                    dis[i.fi] = min(dis[i.fi], dis[u] + i.se); // handle multiple edges
                    pre[i.fi] = u; // record the predecessor
                }
            }
        }
        return cost + dis[t];
    }
    int getShortestPath(int n, vector<vector<int>>& cost)
    {
        graphModeling(n, cost);
        int ans = dijkstra(0, n * n - 1, n, cost[0][0]);
        return ans;
    }
    void printShortestPath(int s, int t)
    {
        stack<int> sta;
        sta.push(t);
        while(1) {
            int q = sta.top();
            sta.push(pre[q]);
            if(pre[q] == 0) break;
        }
        while(!sta.empty()) {
            cout << mp[sta.top()].fi << ' ' << mp[sta.top()].se << endl;
            sta.pop();
        }
    }
private:
    vector<pii> g[N];
    int pre[N], dis[N], vis[N];
    unordered_map<int, pii> mp;
};

整體程式碼

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define pb push_back
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;

const int N = 1e3 + 7;
const int DX[] = {0, 1, 0, -1};
const int DY[] = {1, 0, -1, 0};

struct node
{
    int x, y, w;
    node() {}
    node(int _x, int _y, int _w): x(_x), y(_y), w(_w) {}
    bool operator < (const node& A) const {return w > A.w;}
};
class Solution1 // uniform cost search
{
public:
    bool check(int x, int y, int n) {return x >= 0 && x < n && y >= 0 && y < n;}
    int uniformCostSearch(int n, vector<vector<int>>& cost)
    {
        int vis[n][n];
        memset(vis, 0, sizeof(vis));
        priority_queue<node> pq;
        pq.push(node(0, 0, cost[0][0]));
        while(!pq.empty()) {
            node temp = pq.top(); pq.pop();
            if(temp.x == n - 1 && temp.y == n - 1) return temp.w;
            if(!vis[temp.x][temp.y]) {
                vis[temp.x][temp.y] = 1;
                for(int k = 0; k < 4; ++k) {
                    int tx = temp.x + DX[k], ty = temp.y + DY[k];
                    if(!vis[tx][ty] && check(tx, ty, n))
                        pq.push(node(tx, ty, temp.w + cost[tx][ty]));
                }
            }
        }
        return 0;
    }
    int exe(int n, vector<vector<int>>& cost)
    {
        return uniformCostSearch(n, cost);
    }
};

class Solution2 // graph modeling + Dijkstra
{
public:
    Solution2()
    {
        memset(pre, 0, sizeof(pre));
        memset(dis, inf, sizeof(dis));
        memset(vis, 0, sizeof(vis));
        mp.clear();
    }
    bool check(int x, int y, int n) {return x >= 0 && x < n && y >= 0 && y < n;}
    void graphModeling(int n, vector<vector<int>>& cost)
    {
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < n; ++j) {
                mp[i * n + j] = pii {i, j};
                for(int k = 0; k < 4; ++k) {
                    int tx = i + DX[k], ty = j + DY[k];
                    if(check(tx, ty, n)) g[i * n + j].pb(pii {tx * n + ty, cost[tx][ty]});
                }
            }
        }
    }
    void printNewGraph(int n)
    {
        /*Print the new graph*/
        cout << "The new graph is as follows" << endl;
        for(int i = 0; i < n * n; ++i) {
            cout << i << ": ";
            for(auto j: g[i]) cout << "(" << j.fi << ", " << j.se << ") ";
            puts("");
        }
        puts("");
    }
    int dijkstra(int s, int t, int n, int cost)
    {
        dis[s] = 0; vis[s] = 1;
        for(int i = 0; i < n * n; ++i){
            int mini = inf, u = 0;
            for(int j = 0; j < n * n; ++j)
                if(!vis[j] && dis[j] < mini) mini = dis[j], u = j;
            vis[u] = 1;
            for(auto i: g[u]){
                if(!vis[i.fi] && dis[u] + i.se < dis[i.fi]) {
                    dis[i.fi] = min(dis[i.fi], dis[u] + i.se); // handle multiple edges
                    pre[i.fi] = u; // record the predecessor
                }
            }
        }
        return cost + dis[t];
    }
    int getShortestPath(int n, vector<vector<int>>& cost)
    {
        graphModeling(n, cost);
        int ans = dijkstra(0, n * n - 1, n, cost[0][0]);
        return ans;
    }
    void printShortestPath(int s, int t)
    {
        stack<int> sta;
        sta.push(t);
        while(1) {
            int q = sta.top();
            sta.push(pre[q]);
            if(pre[q] == 0) break;
        }
        while(!sta.empty()) {
            cout << mp[sta.top()].fi << ' ' << mp[sta.top()].se << endl;
            sta.pop();
        }
    }
private:
    vector<pii> g[N];
    int pre[N], dis[N], vis[N];
    unordered_map<int, pii> mp;
};
int main()
{
    int n;
    vector<vector<int>> cost(10, vector<int>(10, 0));
    cin >> n;
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j)
            cin >> cost[i][j];
    //Solution1 s1;
    //cout << s1.exe(n, cost) << endl;
    Solution2 s2;
    cout << s2.getShortestPath(n, cost) << endl;
    s2.printShortestPath(0, n * n - 1);
    return 0;
}
/*
5
1 999 6 10  11
2 999 7 999 12
3 999 8 999 13
5 999 7 999 14
4 5   9 999 10
*/