[题目描述]
给定一张由T条边构成的无向图,点的编号为1~1000之间的整数。
求从起点S到终点E恰好经过N条边(可以重复经过)的最短路。
注意: 数据保证一定有解。
[题目链接] https://www.luogu.com.cn/problem/P2886
[解析]
floyd的变形应用。
边数T<=200,点数N<=10^6,显然应该想到可以对点编号做一个离散化,映射到1~2T范围内。点数骤降,是不是想到了Floyd?Floyd每次选择中间点k插入i->j的最短路相当于最短路增加一条边。
用A[i][j]表示i到j只经过一条路径的最短路,B[i][j]表示从i到j只经过两条路径的最短路,有
B
[
i
]
[
j
]
=
m
i
n
1
<
=
k
<
=
c
n
t
m
i
n
(
B
[
i
]
[
j
]
,
A
[
i
]
[
k
]
+
A
[
k
]
[
j
]
)
B[i][j] = min_{1<=k<=cnt} min(B[i][j],A[i][k]+A[k][j])
B[i][j]=min1<=k<=cntmin(B[i][j],A[i][k]+A[k][j])
int tmp[maxn][maxn];
memset(tmp,0x3f,sizeof(tmp));
for(int k=1;k<=cnt;k++)
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
tmp[i][j] = min(tmp[i][j],x[i][k]+y[k][j]);
用
A
x
[
i
]
[
j
]
A^x[i][j]
Ax[i][j]表示从i到j经过x条路径的最短路,
A
y
[
i
]
[
j
]
A^y[i][j]
Ay[i][j]表示i到j经过y条路径的最短路
有
A
x
+
y
[
i
]
[
j
]
=
m
i
n
1
<
=
k
<
=
c
n
t
m
i
n
(
A
x
+
y
[
i
]
[
j
]
,
A
x
[
i
]
[
k
]
+
A
y
[
k
]
[
j
]
)
A^{x+y}[i][j] = min_{1<=k<=cnt} min(A^{x+y}[i][j],A^x[i][k]+A^y[k][j])
Ax+y[i][j]=min1<=k<=cntmin(Ax+y[i][j],Ax[i][k]+Ay[k][j])
N
<
=
1
0
6
N<=10^6
N<=106,用快速幂加速运算。
代码如下。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 205;
unordered_map<int,int> mp;
int cnt = 0;
int N,T,S,E;
int A[maxn][maxn],B[maxn][maxn];
void read()
{ memset(A,0x3f,sizeof(A));
int u,v,w;
scanf("%d%d%d%d",&N,&T,&S,&E);
for(int i=1;i<=T;i++)
{
scanf("%d%d%d",&w,&u,&v);
if(!mp[u]) mp[u] = ++cnt;
if(!mp[v]) mp[v] = ++cnt;
A[mp[u]][mp[v]] = min(A[mp[u]][mp[v]],w);
A[mp[v]][mp[u]] = A[mp[u]][mp[v]];
}
}
void floyd(int x[maxn][maxn],int y[maxn][maxn])
{
int tmp[maxn][maxn];
memset(tmp,0x3f,sizeof(tmp));
for(int k=1;k<=cnt;k++)
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
tmp[i][j] = min(tmp[i][j],x[i][k]+y[k][j]);
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
x[i][j] = tmp[i][j];
}
void powM(int x)
{
memcpy(B,A,sizeof(A));
while(x)
{
if(x&1)
floyd(A,B);
x>>=1;
floyd(B,B);
}
}
void work()
{
powM(N-1);
printf("%d",A[mp[S]][mp[E]]);
}
int main()
{ read();
work();
return 0;
}