题目:https://www.luogu.org/problemnew/show/P1613
分析:
《最优乘车》(http://www.sqyoj.club/problem.php?id=1005),对本题有帮助。题解见我的博客https://blog.csdn.net/qq_36314344/article/details/88585689。
不能直接求最短路。因为两点间距离为2^t时,可以一秒直达,否则要进行2进制拆分。例如,点1到点n距离是8,则1秒直达;距离是7,则要3秒才能到达。
所以我们先对图进行处理,计算出两点间多少秒能到过,从而想到倍增。为此引入布尔数组f[i][j][t],表示点i,j之间存在长度为2^t的路径,从而可以实现倍增打表,转移方程为f[i][j][t]=f[i][k][t-1]&&f[k][j][t-1]。
因为需要表示两点间有没有连通,为此引入int数组g[i][j],表示点i,j之前的距离,因为松驰的需要,首先初始化g为一个比较大的数——不可过大,否则两个很大的数相加,成了一个负数。
倍增之后,再求最短路,因为最多50个点,所以可以用floyed算法。
AC代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int Maxn=51;
int n,m,x,y,g[Maxn][Maxn],f[Maxn][Maxn][32];
//g[i][j]表示点i,j之的距离
//f[i][j][t]=1表示点i,j之间有2^t的距离的路径
int main(){
cin>>n>>m;
memset(g,0x7,sizeof(g));
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
g[x][y]=f[x][y][0]=1;
}
for(int t=1;t<=32;t++)
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][t-1] && f[k][j][t-1])
g[i][j]=f[i][j][t]=1;
for(int k=1;k<=n;k++)//阶段
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(g[i][k]+g[k][j]<g[i][j])
g[i][j]=g[i][k]+g[k][j];
cout<<g[1][n];
return 0;
}