NOI Online #1 入门组 魔法

全网都是矩阵快速幂,我只会倍增DP

其实这题与 AcWing 345. 牛站 还是比较像的,那题可以矩阵快速幂 / 倍增,这题也行。

\(Floyd\) 预处理两点之间不用魔法最短距离 \(d_{i, j}\) 复杂度 \(O(n^3)\)

然后预处理两点之间至多用一个魔法的最短距离 \(w_{i, j}\),初始为 \(w_{i, j} = d_{i, j}\),枚举 \(i, j\) 和一条边 \((u, v, t)\) \(w_{i, j} = \min(d[i][u] - t + d[v][j])\),复杂度 \(O(n^2m)\)

然后把 \(w\) 数组当做邻接矩阵的新图,所以问题变成了走恰好 \(k\) 条边的最短路(可以理解多走不会变差,因为满足 \(w_{i, i} <= 0\)),这个问题就是 AcWing 345. 牛站 ,具体做法看 AcWing 345. 牛站的倍增 DP 思路,复杂度 \(O(n^3 \log K)\)

注意细节,走 \(0\) 条边的最短路是 \(d_{1, n}\),注意 \(f\) 的初始值。

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

typedef long long LL;

const int N = 105, M = 2505, L = 20;
const LL INF = 1e18;

int n, m, K, l;
LL d[N][N], w[N][N], g[L][N][N], f[N], t[N];

struct E{
	int u, v, w;
} e[M];

int main() {
	memset(g, 0x3f, sizeof g);
	scanf("%d%d%d", &n, &m, &K);
	l = log2(K);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) if (i != j) d[i][j] = INF;
	for (int i = 1; i <= m; i++) {
		scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
		d[e[i].u][e[i].v] = min(d[e[i].u][e[i].v], (LL)e[i].w);
	}
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			w[i][j] = d[i][j];
			for (int k = 1; k <= m; k++)
				w[i][j] = min(w[i][j], d[i][e[k].u] - e[k].w + d[e[k].v][j]);
			g[0][i][j] = w[i][j];
		}
	}
	for (int c = 1; c <= l; c++) 
		for (int i = 1; i <= n; i++) 
			for (int j = 1; j <= n; j++) 
				for (int k = 1; k <= n; k++) 
					g[c][i][j] = min(g[c][i][j], g[c - 1][i][k] + g[c - 1][k][j]);
	for (int i = 1; i <= n; i++) f[i] = d[1][i];
	for (int c = 0; c <= l; c++) {
		if (K >> c & 1) {
			for (int i = 1; i <= n; i++) t[i] = f[i];
			memset(f, 0x3f, sizeof f);
			for (int i = 1; i <= n; i++) 
				for (int j = 1; j <= n; j++) f[i] = min(f[i], t[j] + g[c][j][i]);
		}
	}
	printf("%lld\n", f[n]);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/dmoransky/p/12807869.html
今日推荐