1. 程式人生 > >洛谷 P1613 解題報告

洛谷 P1613 解題報告

HR 輸入輸出 \n long 十分 描述 getchar 題目 數據

P1613 跑路

題目描述

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

\(n\)至少有一條路徑。

輸入輸出格式

輸入格式:

第一行兩個整數\(n\),\(m\),表示點的個數和邊的個數。

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

輸出格式:

一行一個數字,表示到公司的最少秒數。

說明

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

\(100\)%的數據滿足\(n<=50\)\(m<=10000\),最優解路徑長度\(<=\) \(max\) \(long\) \(int\)


首先,要確保自己的語文水平茍的住,這個鬼機器,每秒跑\(2^kkm\)的話是要跑剛好那麽長的,不能多也不能少。

那麽豈不是代表,只有長為\(2^kkm\)

的鏈才算是有效邊嗎?

我們把所有有效邊連上,跑最短路不就行了嘛。

如何求有效邊?

\(2^k?\)有沒有想到什麽?

\(2^k=2^{k-1}+2^{k-1}?\)

對,就是倍增啊!

\(g[u][v][j]\)代表點\(u\)到點\(v\)存在或不存在長度為\(2^j\)的邊。

\(g[u][k][j-1]\)\(g[k][v][j-1]\)同時存在時,
\(g[u][v][j]\)存在。(\(k\)是枚舉的一維)


#include <cstdio>
#include <cstring>
#include <queue>
using namespace
std; const int N=52; int g[N][N][70],n,m; //g[i][j][k]表示i點到j點存在邊權為2^k的路 int g0[N][N]; int read() { int x=0;char c=getchar(); while(c<‘0‘||c>‘9‘) c=getchar(); while(c>=‘0‘&&c<=‘9‘) {x=x*10+c-‘0‘;c=getchar();} return x; } queue <int > q; int used[N],dis[N]; void spfa() { memset(used,0,sizeof(used)); used[1]=1; memset(dis,0x3f,sizeof(dis)); dis[1]=0; q.push(1); while(!q.empty()) { int u=q.front(); q.pop(); used[u]=0; for(int v=1;v<=n;v++) if(g0[u][v]&&dis[v]>dis[u]+g0[u][v]) { dis[v]=dis[u]+g0[u][v]; if(!used[v]) { used[v]=1; q.push(v); } } } } int main() { memset(g,0,sizeof(g)); memset(g0,0,sizeof(g0)); n=read(),m=read(); int u,v; for(int i=1;i<=m;i++) { u=read(),v=read(); g[u][v][0]=1; //f[u][v][0]=v; } for(int j=1;j<=64;j++) for(int k=1;k<=n;k++) for(u=1;u<=n;u++) for(v=1;v<=n;v++) if(g[u][k][j-1]&&g[k][v][j-1]) g[u][v][j]=1; for(u=1;u<=n;u++) for(v=1;v<=n;v++) for(int j=0;j<=64;j++) if(g[u][v][j]) { g0[u][v]=1; break; } spfa(); printf("%d\n",dis[n]); return 0; }

2018.5.2

洛谷 P1613 解題報告