图论:P1613跑路 最短路径 倍增 Floyd

P1613跑路

题目传送门:P1613 跑路 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目:

 

 思路:

  可以考虑倍增的思想,用一个四层循环来计算两个点的路径是不是二的k次方倍,具体思路就是第一层循环循环2的次方,从1循环到64,因为题目说这个机器是用longint存储的,所以最多只能跑2^64,所以只要循环到64就行了,一定要从1->64,从小到大,只有算出来小的,才能算出来大的,算是一个递推的思想,中间两层循环循环区间的端点,也就是起点和终点,在此之前我们要构建一个bool can[i][j][k]数组,代表的是起点为i,终点为j,路径是否为2^k次方,true代表可以,false代表不可以。所以最内层循环就是要循环间断点,x,如果can[i][x][k-1]为true并且can[x][j][k-1]为true,则can[i][j][k]为true,并且还要构建一个dis[i][j]数组,代表i到j的时间,所以dis[i][j]就要改成1.因为每两个k-1次方的区间合并就是k次方,这个思路把图转换为线性的区间DP的样式。然后我们算出所有的dis[i][j],也就是每两个点之间的边权以后,用floyd算法计算最短路径即可,注意要把没有边得两个点的dis初始化为无穷大,便于floyd。

倍增算法:

  

 1  for(int m=1;m<=n;++m)
 2         for(int i=1;i<=n;++i)
 3              for(int j=1;j<=n;++j)
 4                 for(int k=1;k<=n;++k)
 5                 {
 6                     if(can[i][k][m-1]&&can[k][j][m-1])//因为两个路径如果都是2^m-1  合在一起就是2^m
 7                     {
 8                         dis[i][j]=1;
 9                         can[i][j][m]=1;
10                     }
11                 }

 

 

Floyd

 //floyd
    for(int k=1;k<=n;++k)
        for(int j=1;j<=n;++j)
            for(int i=1;i<=n;++i)
            {
                if(dis[i][j]>dis[i][k]+dis[k][j])
                {
                    dis[i][j]=dis[i][k]+dis[k][j];
                }
            }

 

最后的ans就是dp[1][n]了。

 

完整AC代码:

 

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 const int maxn=55;
 6 bool can[maxn][maxn][65];//代表点i和点j之间是否有一条距离为2的k次方的路径
 7 int dis[maxn][maxn];//代表 i 和 j之间的最短路径
 8 int n,m,a,b;
 9 int main()
10 {
11     cin>>n>>m;
12     memset(dis,0x3f,sizeof(dis));//求最短路径 将路径初始化为很大的值
13     for(int i=1;i<=m;++i)
14     {
15         cin>>a>>b;
16         dis[a][b]=1;//2^0=1
17         can[a][b][0]=1;
18     }
19     for(int m=1;m<=n;++m)
20         for(int i=1;i<=n;++i)
21              for(int j=1;j<=n;++j)
22                 for(int k=1;k<=n;++k)
23                 {
24                     if(can[i][k][m-1]&&can[k][j][m-1])//因为两个路径如果都是2^m-1  合在一起就是2^m
25                     {
26                         dis[i][j]=1;
27                         can[i][j][m]=1;
28                     }
29                 }
30 
31     //floyd
32     for(int k=1;k<=n;++k)
33         for(int j=1;j<=n;++j)
34             for(int i=1;i<=n;++i)
35             {
36                 if(dis[i][j]>dis[i][k]+dis[k][j])
37                 {
38                     dis[i][j]=dis[i][k]+dis[k][j];
39                 }
40             }
41     cout<<dis[1][n];
42     return 0;
43 }

 

 

posted @ 2022-05-07 11:40  朱朱成  阅读(50)  评论(0编辑  收藏  举报