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