题目链接:Walking Plan
题意
给一个 n n 个节点 条边的有向图,第 i i 条边的两个端点为 ,边的长度为 wi w i , q q 次询问,每次询问从节点 到 t t 至少走过 条路径的最小距离。
输入
第一行包含一个整数 T (1≤T≤10) T ( 1 ≤ T ≤ 10 ) ,接下去有 T T 组数据,每组数据第一行为两个整数 ,接下去 m m 行每行三个整数 ,接着为一个整数 q (1≤q≤105) q ( 1 ≤ q ≤ 10 5 ) ,接下去 q q 行每行三个整数 。
输出
对于每次询问,输出最短路径长度,如果无法从节点 si s i 到达 ti t i ,则输出 −1 − 1 。
样例
输入 |
---|
2 3 3 1 2 1 2 3 10 3 1 100 3 1 1 1 1 2 1 1 3 1 2 1 1 2 1 1 2 1 1 |
输出 |
111 1 11 -1 |
题解
定义 G[i][j] G [ i ] [ j ] 为从节点 i i 恰好经过 步到达节点 j j 的最短距离(即原图按输入取最小值),无法到达设为 , dis[k][i][j] d i s [ k ] [ i ] [ j ] 表示从 i i 点出发恰好经过 步到达 j j 点的最短路径,则有递推式:
定义 A[x][i][j] A [ x ] [ i ] [ j ] 表示从 i i 恰好经过 步到达 j j 的最短距离, 表示从 i i 最少经过 次到达 j j 的最短距离,这样 就可以分为 ⌊k100⌋ ⌊ k 100 ⌋ 和 k%100 k % 100 两个部分,通过枚举中转点得到答案:
ans=min(A[⌊k100⌋][i][u]+B[k%100][u][j]),u∈[1,n] a n s = min ( A [ ⌊ k 100 ⌋ ] [ i ] [ u ] + B [ k % 100 ] [ u ] [ j ] ) , u ∈ [ 1 , n ]第一部分可以直接从上面的递推式得到,第二部分可以在原图上令 G[i][i]=0,i∈[1,n] G [ i ] [ i ] = 0 , i ∈ [ 1 , n ] 后跑一遍 floyd f l o y d ,再将从 i i 恰好经过 步到达 j j 的最短距离距离 通过 G[i][j] G [ i ] [ j ] 转化为 B[x][i][j] B [ x ] [ i ] [ j ] :
B[k][i][j]=min(dis[k][i][u],G[u][j]),u∈[1,n] B [ k ] [ i ] [ j ] = min ( d i s [ k ] [ i ] [ u ] , G [ u ] [ j ] ) , u ∈ [ 1 , n ]
过题代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <algorithm>
#include <functional>
#include <iomanip>
using namespace std;
#define LL long long
const int Size = 51;
const int maxn = 101;
int n;
struct Matrix {
int num[Size][Size];
void Init() {
for(int i = 1; i <= n; ++i) {
memset(num[i], 0x3f, sizeof(int) * (n + 1));
}
}
void Set_zero() {
for(int i = 1; i <= n; ++i) {
num[i][i] = 0;
}
}
void operator=(const Matrix &m) {
for(int i = 1; i <= n; ++i) {
memcpy(num[i], m.num[i], sizeof(int) * (n + 1));
}
}
void Combine(const Matrix &m, Matrix &ans) {
Matrix ret;
ret.Init();
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
for(int k = 1; k <= n; ++k) {
ret.num[i][j] = min(ret.num[i][j], num[i][k] + m.num[k][j]);
}
}
}
ans = ret;
}
void floyd() {
for(int k = 1; k <= n; ++k) {
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
num[i][j] = min(num[i][j], num[i][k] + num[k][j]);
}
}
}
}
};
int T, m, u, v, dis, q, k, INF, ans;
Matrix A[maxn], B[maxn], G;
int main() {
#ifdef LOCAL
freopen("test.txt", "r", stdin);
// freopen("test1.out", "w", stdout);
#endif // LOCAL
ios::sync_with_stdio(false);
memset(&INF, 0x3f, sizeof(int));
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
G.Init();
for(int i = 0; i < m; ++i) {
scanf("%d%d%d", &u, &v, &dis);
G.num[u][v] = min(G.num[u][v], dis);
}
A[0].Init();
A[0].Set_zero();
B[0].Init();
B[0].Set_zero();
for(int i = 1; i < maxn; ++i) {
B[i - 1].Combine(G, B[i]);
}
for(int i = 1; i < maxn; ++i) {
A[i - 1].Combine(B[100], A[i]);
}
G.Set_zero();
G.floyd();
for(int i = 0; i < maxn; ++i) {
G.Combine(B[i], B[i]);
}
scanf("%d", &q);
while(q--) {
scanf("%d%d%d", &u, &v, &k);
ans = INF;
for(int i = 1; i <= n; ++i) {
ans = min(ans, A[k / 100].num[u][i] + B[k % 100].num[i][v]);
}
if(ans == INF) {
printf("-1\n");
} else {
printf("%d\n", ans);
}
}
}
return 0;
}