345. 牛站(Floyd倍增算法,Floyd快速幂)

345. 牛站 - AcWing题库

给定一张由 T 条边构成的无向图,点的编号为 1∼1000 之间的整数。

求从起点 S 到终点 E 恰好经过 N 条边(可以重复经过)的最短路。

注意: 数据保证一定有解。

输入格式

第 1 行:包含四个整数 N,T,S,E。

第 2..T+1 行:每行包含三个整数,描述一条边的边长以及构成边的两个点的编号。

输出格式

输出一个整数,表示最短路的长度。

数据范围

2≤T≤100
2≤N≤106

输入样例:
2 6 6 4
11 4 6
4 4 8
8 4 9
6 6 8
2 6 9
3 8 9
输出样例:
10

解析: 

Floyd的倍增算法的经典运用:

第一步:离散化

第二步:

Floyd算法中d[k,i,j]表示:从i到j经过的点的编号不大于k的最短路径。

Floyd的倍增算法中d[k,i,j]表示:从i到j,恰好经过k条边的最短路径。

状态转移方程:

d[a+b,i,j]=min{d[a,i,k]+d[b,k,j]}, k=1~n

直接按照上述过程进行求解的话我们需要四个循环,反别枚举d[k,i,j]中的三个变量,还有一个循环用于枚举中间点,将k一分为二。

但这里我们可以考虑使用倍增(或者叫快速幂)的方式进行处理,因为其运算过程不考虑先后顺序(满足结合律)。

时间复杂度为O(n^3*log2(N))

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N = 205;
int n, T, S, E;
int g[N][N];
map<int, int>mp;
int ret[N][N];
int cnt;

void mul(int c[][N], int a[][N], int b[][N]) {
	static int temp[N][N];
	memset(temp, 0x3f, sizeof temp);
	for (int k = 1; k <= cnt; k++) {
		for (int i = 1; i <= cnt; i++) {
			for (int j = 1; j <= cnt; j++) {
				temp[i][j] = min(temp[i][j], a[i][k] + b[k][j]);
			}
		}
	}
	memcpy(c, temp, sizeof temp);
}

void qmi() {
	memset(ret, 0x3f, sizeof ret);
	for (int i = 1; i < N; i++)ret[i][i] = 0;
	
	while (n) {
		if (n & 1)mul(ret, ret, g);
		mul(g, g, g);
		n >>= 1;
	}
}

int main() {
	cin >> n >> T >> S >> E;
	memset(g, 0x3f, sizeof g);
	for (int i = 1,a,b,c; i <= T; i++) {
		scanf("%d%d%d", &c, &a, &b);
		if (!mp.count(a))mp[a] = ++cnt;
		if (!mp.count(b))mp[b] = ++cnt;
		g[mp[a]][mp[b]] = g[mp[b]][mp[a]] = min(g[mp[a]][mp[b]], c);
	}
	qmi();

	cout << ret[mp[S]][mp[E]] << endl;
	return 0;
}

 

posted @ 2024-01-21 17:33  Landnig_on_Mars  阅读(7)  评论(0编辑  收藏  举报  来源