大致题意:给一张有向图(存在自环),每条边权均为1,现在有一人要从1号结点走到n号结点,但是这个人有一个神奇的瞬移机器,这个机器走
2
k
2^k
2k(
k
k
k为自然数)花费的时间都为1,问从起点到终点的最小花费时间。
思路如下:
step 1.我们可以处理出所有的从一个点到达另一个点的距离可以为
2
k
2^k
2k的路径,并把这两个点之间连一条权值为1的边。
step 2.在我们新得到的图上运行最短路算法,求出从起点到达终点花费的最少时间。
下面我们来看看如何处理step 1:
状态表示:用
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]记录能否通过
2
k
2^k
2k的距离从
i
i
i到达
j
j
j.
转移方程:
i
f
(
f
[
i
]
[
u
]
[
k
−
1
]
if(f[i][u][k-1]
if(f[i][u][k−1]&&
f
[
u
]
[
j
]
[
k
−
1
]
)
f[u][j][k-1])
f[u][j][k−1]) 则
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]为真。
需要注意:因为题意已经给出任意边的边权均为1,则可以用反证法证明:若
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]为真,则一定存在中间结点使得
f
[
i
]
[
u
]
[
k
−
1
]
f[i][u][k-1]
f[i][u][k−1]&&
f
[
u
]
[
j
]
[
k
−
1
]
f[u][j][k-1]
f[u][j][k−1]为真。
代码如下:
#include<cstring>
#include<iostream>
using namespace std;
const int N = 100, M = 20000;
const int K = 35;
int f[N][N][K];
int g[N][N];
int n,m;
int main()
{
memset(f,0x3f,sizeof f);
memset(g,0x3f,sizeof g);
cin>>n>>m;
for(int i=1;i<=m;++i)
{
int a,b;
cin>>a>>b;
f[a][b][0]=1;
g[a][b]=1;
}
for(int u=1;u<=32;++u)
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(f[i][k][u-1]==1&&f[k][j][u-1]==1)
{
f[i][j][u]=1;
g[i][j]=1;
}
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
g[i][j]=min(g[i][k]+g[k][j],g[i][j]);
cout<<g[1][n]<<endl;
return 0;
}