分層圖最短路


分層圖最短路,就是在分層圖上解決最短路問題
一般模型為:
在一張圖上,有k次機會可以通過一條邊而不需要計算權值(免費過路),求從起點到終點的最短路線
常規思路:
想象將一個點拆分為k + 1個點,分別表示到這個點時,免費權消耗了0次,1次,2次......k次
這樣實際我們可以把這k個點想象成對應dp的不同的狀態
dis[i][j]表示到第i個點時,消耗了j次乘車權后的最短路線
我們用to表示要到達的點,x表示父親節點,就有
dis[to][j] = min(dis[x][j] + val(x, to), dis[x][j - 1])
因為我們跑最短路時是從前向后跑,也就是當前狀態推出后繼狀態,所以實際上我們可以推出兩個可能狀態
如果我們消耗了免費過路權
dis[to][j] = min{dis[x][j - 1]}
如果我們沒消耗免費過路權
dis[to][j] = min{dis[x][j] + val(x, to)}
這就提醒我們,我們的隊列在加入到達某個點的同時,要分別記錄到達這個點時的兩種不同的狀態,以免造成情況遺漏
也就是q[i][j]表示到第i個點時,第i個點在j的情況下我們消耗了幾次免費過路權,j為0或是1,0表示沒有消耗免費過路權,1表示消耗了免費過路權
到這里我們就能與上面的拆點聯系上了,我們想,到了終點時,可能有:用了0次免費過路權,用了1次免費過路權,用了2次,用了3次....用了k次
也就是k+1種可能狀態,此時我們把這k+1種狀態,每種狀態都想象成原本的這個點拆分出來的一個點,也就相當於這個點拆分出了k+1個點,就和上面接上了
然后我們合理外推,對於每一個點都可能出現這樣的情況,也就相當於每一個點都拆分成了k+1個點,這n*(k+1)個點之間彼此連接,跑最短路,這樣可能有點抽象
實際上把這想象成一個dp的過程是最好理解的

例題1:move(集訓考試題)
題目描述
給定一張地圖一共有 n 個城市,城市編號為 0 ~ n - 1,這 n 個城市通過 m
條鐵路連接(走一條鐵路視為乘車一次)。而小A 想從城市 s 出發,到 達城市 t
結束。小A 可以免費乘車 k 次,現在他想知道,他這 次旅游的最少花費是多少?
輸入格式
第一包含三個整數 n, m, k,含義見題目描述。
第二有兩個整數 s, t,表示小A 的出發城市和結束城市。
接下來 m 行,每行三個整數 x, y, z,表?在城市 x 和 y 之間有一條鐵路相
連,乘車花費為 z。
輸出格式
輸出一行,一個整數表示答案。
樣例輸入
5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100
樣例輸出
8
數據范圍
對於 30% 的數據, 2 ≤ n ≤ 50, 1 ≤ m ≤ 300, k = 0;
對於 50% 的數據, 2 ≤ n ≤ 600, 1 ≤ m ≤ 6000, 0 ≤ k ≤ 1;

很標准的模板型題目。
思路就是上面的思路,不加贅述,代碼實現口胡不好說,直接讀代碼吧=-=

  1 #include<iostream>
  2 #include<iomanip>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<ctime>
  6 #include<cmath>
  7 #include<algorithm>
  8 #include<cstdlib> 
  9 using namespace std;
 10 const int maxn = 100086;
 11 const int inf = 1000000007;
 12 struct node {
 13     int y, net, v;
 14 }e[maxn];
 15 int dis[maxn][11];
 16 int n, m, k, st, ed;
 17 int lin[maxn], len = 0;
 18 int q[maxn][2];//第二維, 0表示當前點沒有拆點,1表示當前點進行了拆點,拆成了k+1個點q[i][1]存儲的是第i個點拆出的點的編號 
 19 bool vis[maxn][11];
 20 
 21 inline int read() {
 22     int x = 0, y = 1;
 23     char ch = getchar();
 24     while(!isdigit(ch)) {
 25         if(ch == '-') y = -1;
 26         ch = getchar();
 27     }
 28     while(isdigit(ch)) {
 29         x = (x << 1) + (x << 3) + ch - '0';
 30         ch = getchar();
 31     }
 32     return x * y;
 33 }
 34 
 35 inline void insert(int xx, int yy, int vv) {
 36     e[++len].net = lin[xx];
 37     e[len].v = vv;
 38     e[len].y = yy;
 39     lin[xx] = len;
 40 }
 41 
 42 inline void spfa(int st) {
 43     int head = 0, tail = 1;
 44     for(int i = 0; i < n; ++i)
 45         for(int j = 0; j <= k; ++j)
 46             dis[i][j] = inf;
 47     int x, j;//j實際表示使用了多少次免費乘車權,//我們將一個點拆了多次,j同時作為這些被拆出的點的編號 
 48     memset(vis, 0, sizeof(vis));
 49     vis[st][0] = 1, dis[st][0] = 0;
 50     q[1][0] = st, q[1][1] = 0;
 51     while(head != tail) {
 52         head = (head + 1) % 100003;
 53         x = q[head][0];
 54         j = q[head][1];
 55         //vis[x][j] = 1;
 56         for(int i = lin[x]; i; i = e[i].net) {
 57             int to = e[i].y;
 58             if(dis[x][j] + e[i].v < dis[to][j]) {//如果x到to沒有使用免費乘車權 
 59                 dis[to][j] = dis[x][j] + e[i].v;
 60                 if(!vis[to][j]) {
 61                     vis[to][j] = 1;
 62                     tail = (tail + 1) % 100003;
 63                     q[tail][0] = to;
 64                     q[tail][1] = j;
 65                 } 
 66             } 
 67             if(j < k && dis[x][j] < dis[to][j + 1]) {//如果x到to使用了免費乘車權 
 68                 dis[to][j + 1] = dis[x][j];
 69                 if(!vis[to][j + 1]) {
 70                     vis[to][j + 1] = 1;
 71                     tail = (tail + 1) % 100003;
 72                     q[tail][0] = to;
 73                     q[tail][1] = j + 1;
 74                 }
 75             }
 76         }
 77         vis[x][j] = 0;
 78     }
 79 }
 80 
 81 int main() {
 82 //    freopen("move.in", "r", stdin);
 83 //    freopen("move.out", "w", stdout);
 84     n = read(), m = read(), k = read();
 85     st = read(), ed = read();
 86     for(int i = 1; i <= m; ++i) {
 87         int x, y, v;
 88         x = read(), y = read(), v = read();
 89         insert(x, y, v);
 90         insert(y, x, v);
 91     }
 92     spfa(st);
 93     int ans = inf;
 94     for(int i = 0; i <= k; ++i)
 95         ans = min(ans, dis[ed][i]);
 96     cout << ans << '\n';
 97 //    fclose(stdin);
 98 //    fclose(stdout);
 99     return 0;
100 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM