1. 程式人生 > 其它 >圖論:P1613跑路 最短路徑 倍增 Floyd

圖論: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 }