解题思路
考虑DP,设
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]表示从i到j走k条路的最短路径长度,易得转移方程:
f
[
i
]
[
j
]
[
k
]
=
m
i
n
(
f
[
i
]
[
t
]
[
k
−
1
]
+
f
[
t
]
[
j
]
[
1
]
)
f[i][j][k]=min(f[i][t][k-1]+f[t][j][1])
f[i][j][k]=min(f[i][t][k−1]+f[t][j][1])。
因为在转移时
k
k
k只与
k
−
1
k-1
k−1有关,所以可以化简掉第三维
f
[
i
]
[
j
]
=
m
i
n
(
f
[
i
]
[
t
]
+
f
[
t
]
[
j
]
)
f[i][j]=min(f[i][t]+f[t][j])
f[i][j]=min(f[i][t]+f[t][j])。这个形式与矩阵快速幂类似,所以走n条路就等价于把状态数组视为矩阵进行n次的矩阵乘幂,最后答案为
f
[
s
]
[
e
]
f[s][e]
f[s][e]。
PS:需要离散化!!
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#define ll long long
#define ldb long double
using namespace std;
int n,t,s,e,w,x,y,tot,num[10010];
struct c{
int a[500][500];
}A,B;
c operator *(const c &A,const c &B){
c CC;
memset(CC.a,0x3f,sizeof(CC.a));
for(int k=1;k<=tot;k++)
for(int i=1;i<=tot;i++)
for(int j=1;j<=tot;j++)
CC.a[i][j]=min(CC.a[i][j],A.a[i][k]+B.a[k][j]);
return CC;
}
void poww(int x){
if(x==1)
{
B=A;
return;
}
poww(x>>1);
B=B*B;
if(x&1)
B=B*A;
}
int main(){
memset(A.a,0x3f,sizeof(A.a));
memset(B.a,0x3f,sizeof(B.a));
scanf("%d%d%d%d",&n,&t,&s,&e);
for(int i=1;i<=t;i++)
{
scanf("%d%d%d",&w,&x,&y);
if(!num[x])
num[x]=++tot;
if(!num[y])
num[y]=++tot;
A.a[num[x]][num[y]]=A.a[num[y]][num[x]]=w;
}
poww(n);
printf("%d",B.a[num[s]][num[e]]);
}