Luogu P1613 跑路

题目用倍增进行确定路径, Floyd来找到答案。

用一个三元数组f[i][j[k], i 与 j 表示从点i到点j,k表示这条路的长度为2 ^ k,当f[i][j][k] = 1时表示存在这样一条路,反正则无。 这样预处理后,跑一次Floyd就可以得出答案。

有一点需要注意,题目中m <= 10000, 而2的14次方已经大于10000,但在倍增时外循环的值k却要大于14才能AC此题的原因是如图

我们可以发现把那个环跑两次就可以得到16的路程得到答案1,这时2 ^ 4大于边数10.所以由此可以推理当把环的长度变大时可能可以凑出一个k,这时2 ^ k 远大于10000.

#include<iostream>
#include<cstdio>
using namespace std;

const int inf = 777777;

int n, m;
int f[51][51][50], ans[51][51];

template<typename T>
inline void read(T &x){
	x = 0;
	T op = 1;
	char c = getchar();
	for(; c < '0' || c > '9'; c = getchar())
		if(c == '-')	op = -1;
	for(; c <= '9' && c >= '0'; c = getchar())
		x = (x << 3) + (x << 1) + c - '0';
	x *= op; 
} 

int main(){
	read(n), read(m);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j){
			if(i != j)	ans[i][j] = inf;
			else ans[i][j] = 0;
		}
	for(int i = 1; i <= m; ++i){
		int x, y;
		read(x), read(y);
		f[x][y][0] = 1;
		if(x != y) ans[x][y] = 1;	
	}
	for(int k = 1; k < 50; ++k)
		for(int v = 1; v <= n; ++v)
			for(int i = 1; i <= n; ++i)
				for(int j = 1; j <= n; ++j)
					if(f[i][v][k - 1] && f[v][j][k - 1]){
						f[i][j][k] = 1;
						if(i != j) ans[i][j] = 1;
					}
	for(int k = 1; k <= n; ++k)
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= n; ++j)
				ans[i][j] = min(ans[i][j], ans[i][k] + ans[k][j]);
	printf("%d\n", ans[1][n]);
	return 0;
}
posted @ 2019-11-05 20:00  ZmeetL  阅读(99)  评论(0)    收藏  举报