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;
}

posted @ 2020-10-18 17:47  tuscjaf  阅读(69)  评论(0编辑  收藏  举报