题目传送门:https://www.luogu.org/problemnew/show/P1613
题意:
有n个点,m条边,每条边权为1个单位,每秒走1个单位,每走2的幂次方各单位需要1秒,求最短用时。
思路:
既然题目说是倍增,就说这种方法叫倍增(尽管我个人认为这不叫倍增)吧。
贪心地,我们可以知道尽量走2的幂次方的边,如1,2,4,8……用时为1。倍增思想可以知道(尽管我认为这是数学思想),每两个相同的2的幂次方数为1个2的幂次方数,如2+2=2^2,4+4=2^3。所以,对于三个点,两条边连接,如果这两条边边权为两个相等的2的幂次方数,那么,走过这两条边的时间为1,所以,我们可以从这两条边的起点向终点连一条边权(时间)为1的边。最后,跑一边最短路即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int f[100][100];//f[i][j]表示点i到点j之间的最短路(时间)
bool bz[100][100][100];//f[i][j][p]表示从点i到点j之间有一条边权为2^p的边
int main()
{
int x,y;
scanf("%d %d",&n,&m);
memset(f,63,sizeof(f));
for(int i=1;i<=m;i++)
{
scanf("%d %d",&x,&y);
f[x][y]=1;
bz[x][y][0]=true;
}
for(int p=1;p<=32;p++)
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(bz[i][k][p-1]&&bz[k][j][p-1]) bz[i][j][p]=true,f[i][j]=1;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
printf("%d",f[1][n]);
}