题目:POJ3613.
题目大意:给出一张
t
t
t个点的图,然你求出经过
n
n
n条边后,
S
S
S到
T
T
T的最短路.
1
≤
n
≤
1
0
6
,
1
≤
t
≤
100
1\leq n\leq 10^6,1\leq t\leq 100
1≤n≤106,1≤t≤100.
这道题一开始觉得挺容易的,用 f [ i ] [ j ] f[i][j] f[i][j]表示从起点到点 i i i经过 j j j的最短路,不断更新就可以了.
但是突然发现数据巨大根本跑不过去,然后就开始看书上的题解了…
书上说的思路就是先离散化,然后用floyd算法用 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示从 i i i到 j j j的经过 k k k条边的最短路.
那么我们将转移方程列出来:
f
[
i
]
[
j
]
[
k
]
=
m
i
n
{
f
[
i
]
[
t
]
[
l
]
+
v
[
t
]
[
j
]
[
k
−
l
]
}
f[i][j][k]=min\{f[i][t][l]+v[t][j][k-l]\}
f[i][j][k]=min{f[i][t][l]+v[t][j][k−l]}
然后我们会发现,每一次转移时用来更新的边权都是一样的,而且点的范围特别小,更新的次数特别多.
所以我们可以想到矩阵乘法刚好满足这几个性质,而且 f f f其实就是一个矩阵!
那么其实 f f f就是一个“广义矩阵乘法”,很明显广义矩阵乘法是满足结合律的,所以我们就可以用快速幂优化递推.时间复杂度 O ( P 3 l o g T ) O(P^3logT) O(P3logT),由于 2 P 2P 2P个点不一定能跑满,所以这个算法是能过的.
代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int P=1000,T=100,INF=(1<<29)-1;
int num[P+9],ord[P+9],top;
int s,t,x[T+9],y[T+9],v[T+9];
int n,m;
struct matrix{
int a[T*2+9][T*2+9],n,m;
matrix(){
n=m=0;
for (int i=0;i<T*2+9;i++)
for (int j=0;j<T*2+9;j++)
a[i][j]=INF;
}
matrix operator * (const matrix &p)const{
matrix tmp=matrix();
tmp.n=n;tmp.m=p.m;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=1;k<=p.m;k++)
tmp.a[i][k]=min(tmp.a[i][k],a[i][j]+p.a[j][k]);
return tmp;
}
}a;
matrix power(matrix a,int k){
matrix s=a;k--;
for (;k;k>>=1,a=a*a)
if (k&1) s=s*a;
return s;
}
Abigail into(){
scanf("%d%d%d%d",&n,&m,&s,&t);
for (int i=1;i<=m;i++){
scanf("%d%d%d",&v[i],&x[i],&y[i]);
num[x[i]]++;num[y[i]]++;
}
}
Abigail work(){
for (int i=1;i<=1000;i++)
if (num[i]) ord[i]=++top;
s=ord[s];t=ord[t];
for (int i=1;i<=m;i++)
a.a[ord[x[i]]][ord[y[i]]]=a.a[ord[y[i]]][ord[x[i]]]=min(a.a[ord[x[i]]][ord[y[i]]],v[i]);
a.n=a.m=top;
a=power(a,n);
}
Abigail outo(){
printf("%d\n",a.a[s][t]);
}
int main(){
into();
work();
outo();
return 0;
}