題目描述
小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]);
}