題目傳送門
大致題意:
給定一張\(n\)個結點\(m\)條邊的有向圖,邊權固定為1,每秒可以移動\(2^t\)(\(t\)為任意值)。求從\(1\)到\(n\)的最短所需時間。
其中\(n\leq 50,m\leq 10000,dis_\leq int_\)。
第一眼是個最短路,然後發現不對勁。
因為從\(1\)到\(n\)的最短路不一定是所求答案,即二進位制下\(1\)的個數不一定最少。
舉個栗子:
在這張圖中,很明顯最短路是\(1-2-4-7\),但需要\(2+1\)共\(2\)秒。而另一條路雖然長度為\(4\),但只需要一次\(2^2\)就可以到達,時間只需要\(1\)秒。
於是我們需要根據題目中\(2^t\)這個提示,考慮如何融入倍增的思想。
最終的演算法肯定還是落腳在最短路上,但是這個邊不應該是最初輸入進來的,而是通過我們的計算,可以一步到達的兩個點之間連邊。
什麼情況下兩個點可以一步到達?
對於\((u,v)\)是否能通過乙個\(2^t\)跨過去,我們如果能找到乙個\(k\)滿足\((u,k)\)能夠\(2^\)跨過去,並且,\((k,v)\)
也可以\(2^\)跨過去,那就一定可行。
於是考慮用\(dp[u][v][t]\)表示,從\(u\)到\(v\)是否能移動\(2^t\)到達。
對於輸入的邊\((u,v)\),當然是可以通過\(2^0\)距離到達的,那麼\(dp[u][v][0]=1\)。並連上\(u\)和\(v\)。
此外就如上所述,遍歷其他點\(k\),如果有dp[u][k][t-1]&dp[k][v][t-1]==1
,那麼dp[u][v][t]=1
,並且把\(u\),\(v\)連上。
完成了建邊之後,直接跑最短路即可。考慮到\(n\)的範圍只有\(50\),於是方便地選用\(floyd\)。
另外,\(t\)只需要列舉到\(31\)即可(\(int_=2^-1\))。
code:
#includeusing namespace std;
const int n=70;
int dis[n][n],dp[n][n][n];
int main()
for(int t=1;t<32;t++)
for(int k=1;k<=n;k++)
for(int u=1;u<=n;u++)
for(int v=1;v<=n;v++)
if(dp[u][k][t-1]&dp[k][v][t-1])
dis[u][v]=dp[u][v][t]=1;
for(int k=1;k<=n;k++)
for(int u=1;u<=n;u++)
for(int v=1;v<=n;v++)
dis[u][v]=min(dis[u][v],dis[u][k]+dis[k][v]);
printf("%d\n",dis[1][n]);
return 0;
}
洛谷 P1613 跑路(倍增 最短路)
小a的工作不僅繁瑣,更有苛刻的規定,要求小a每天早上在6 00之前到達公司,否則這個月工資清零。可是小a偏偏又有賴床的壞毛病。於是為了保住自己的工資,小a買了乙個十分牛b的空間跑路器,每秒鐘可以跑2 k千公尺 k是任意自然數 當然,這個機器是用longint存的,所以總跑路長度不能超過maxlong...
洛谷P1613 跑路 最短路 倍增
小a的工作不僅繁瑣,更有苛刻的規定,要求小a每天早上在6 00之前到達公司,否則這個月工資清零。可是小a偏偏又有賴床的壞毛病。於是為了保住自己的工資,小a買了乙個十分牛b的空間跑路器,每秒鐘可以跑2 k千公尺 k是任意自然數 當然,這個機器是用longint存的,所以總跑路長度不能超過maxlong...
洛谷 P1613 跑路(DP 倍增 最短路)
小a的工作不僅繁瑣,更有苛刻的規定,要求小a每天早上在6 00之前到達公司,否則這個月工資清零。可是小a偏偏又有賴床的壞毛病。於是為了保住自己的工資,小a買了乙個十分牛b的空間跑路器,每秒鐘可以跑2 k千公尺 k是任意自然數 當然,這個機器是用longint存的,所以總跑路長度不能超過maxlong...