1. 程式人生 > 實用技巧 >P1613 跑路 TJ

P1613 跑路 TJ

題目連結

思路

由題目 \(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\)

,然後 \(DP\)
由於是倍增,容易得到狀態轉移方程 \(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;
}