洛谷P1613 跑路(倍增+floyd)

題目描述

小A的工作不僅繁瑣,更有苛刻的規定,要求小A每天早上在6:00之前到達公司,否則這個月工資清零。可是小A偏偏又有賴牀的壞毛病。於是爲了保住自己的工資,小A買了一個十分牛B的空間跑路器,每秒鐘可以跑2^k千米(k是任意自然數)。當然,這個機器是用longint存的,所以總跑路長度不能超過maxlongint千米。小A的家到公司的路可以看做一個有向圖,小A家爲點1,公司爲點n,每條邊長度均爲一千米。小A想每天能醒地儘量晚,所以讓你幫他算算,他最少需要幾秒才能到公司。數據保證1到n至少有一條路徑。

輸入輸出格式

輸入格式: 第一行兩個整數n,m,表示點的個數和邊的個數。

接下來m行每行兩個數字u,v,表示一條u到v的邊。

輸出格式: 一行一個數字,表示到公司的最少秒數。

輸入輸出樣例

輸入樣例
# 1:
4 4
1 1
1 2
2 3
3 4
輸出樣例
#1:
1
說明:

【樣例解釋】

1->1->2->3->4,總路徑長度爲4千米,直接使用一次跑路器即可。

【數據範圍】

50%的數據滿足最優解路徑長度<=1000;

100%的數據滿足n<=50,m<=10000,最優解路徑長度<=maxlongint。

題解:
首先對於這個題來說,當從起點到終點距離出現2^k時肯定最優(事實上在洛谷直接輸出1就有40分),關鍵就是判斷什麼時候出現2^k。我們可以利用倍增的思想,2^(k-1)+2^(k-1)=2^k,參照Floyd,當i~k的路長爲2的倍數,k~j的路長爲2的倍數,則i~j的路長也爲2的倍數,所以可以先預處理出所有的長度(對於從一個點跑到另一個點如果路長爲2的倍數則dis=1)。我們設g[i][u][v]表示u到v能否通過2^i到達,這也就是1s,在倍增的時候順便處理處dis[i][j],然後只需要跑一邊Floyd即可得到答案。
代碼如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring> 
using namespace std;
const int N=105;
int dis[N][N];
bool g[N][N][N]; 
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    memset(g,0,sizeof(g));
    memset(dis,63,sizeof(dis));
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        dis[x][y]=1;
        g[x][y][0]=1;
    }
    for(int s=1;s<=64;s++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    for(int k=1;k<=n;k++)
        if(g[i][j][s-1]&&g[j][k][s-1])
        {
            g[i][k][s]=1;
            dis[i][k]=1;
        }
    for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    printf("%d\n",dis[1][n]);
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章