P1613 跑路 TJ
阿新 • • 發佈:2020-10-18
思路
由題目 \(2^k\) 我們很容易聯想到倍增,此時我們可以倍增\(+Floyd\),解決該問題。
首先我們設 \(g[i][j]\) 表示 \(i\) 和 \(j\) 之間的路程,顯然,如果 \(g[i][j]\) 和 \(g[j][k]\) 同時為 \(1\) 我們就可以將 \(g[i][k]\) 設定成 \(1\),
由此我們新建一個圖 \(t[i][j][k]\) 表示 \(i\) 和 \(j\) 之間是否有 \(2^k\) 的路,如果有,就可以相應的把 \(g[i][j]\) 設定成 \(1\),
此時我們可以初始化:如果 \(g[i][j] = 1\) 那麼 \(t[i][j][0] = 1\)
由於是倍增,容易得到狀態轉移方程 \(t[i][j][k] = (t[i][x][k - 1] = t[x][j][k - 1] = 1~ ? ~1 : 0)\),然後根據 \(t[i][j][k]\) 更新 \(g[i][j]\)。
最後用 \(g\) 進行一次 \(Floyd\) 即可
Folyd
還是利用 \(DP\) 的思想,每次加進去一個點,狀態轉移方程:\(g[i][j] = \min (g[i][j] ,g[i][k] + g[k][j])\)。
程式碼
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 51; const int MAXM = 10010; int n ,m; int t[MAXN][MAXN][64]; int g[MAXN][MAXN]; void solve () { for (int k = 1;k <= 64;++ k) { for (int x = 1;x <= n;++ x) { for (int q = 1;q <= n;++ q) { for (int w = 1;w <= n;++ w) { if (t[q][x][k - 1] == 1 && t[x][w][k - 1] == 1) { t[q][w][k] = 1; g[q][w] = 1; } } } } } } void Floyed () { for (int k = 1;k <= n;++ k) { for (int q = 1;q <= n;++ q) { for (int w = 1;w <= n;++ w) { g[q][w] = min (g[q][w] ,g[q][k] + g[k][w]); } } } return ; } int main () { memset (g ,0x3f ,sizeof (g)); memset (t ,0 ,sizeof (t)); scanf ("%d%d",&n ,&m); int _from ,_to; for (int q = 1;q <= m;++ q) { scanf("%d%d",&_from ,&_to); g[_from][_to] = 1; t[_from][_to][0] = 1;//2 ^ 0 = 1 } solve (); Floyed (); printf ("%d\n",g[1][n]); return 0; }