2017多校第4场 懒人跑步(spfa+思维题)
题目描述
在ZJU,每个学生都被要求课外跑步,并且需要跑够一定的距离 K ,否则体育课会挂科。
ZJU有4个打卡点,分别标记为 p1,p2,p3,p4 。每次你到达一个打卡点,你只需要刷一下卡,系统会自动计算这个打卡点和上一个打卡点的距离,并将它计入你的已跑距离。
系统把这4个打卡点看成一个环。 p1 与 p2 相邻、 p2 与 p3 相邻、 p3 与 p4 相邻、 p4 与 p1 相邻。当你到达打卡点 pi 时,你只能跑到与该打卡点相邻的打卡点打卡。
打卡点 p2 是离宿舍最近的一个打卡点。CJB总是从 p2 出发,并回到 p2 。因为CJB很圆,所以他希望他跑的距离不少于 K ,但又要尽量小。
题解
取 d=min(dis1-2,dis2-3),那么对于每种方案,均可以通过往返跑 d 这条边使得距离增加 2d
令 w=2d,dp[i][j] 表示从起点出发到达 i ,距离模 w 为 j 时的最短路,这个由 spfa 可求得,时间复杂度为 O(w logw)
最后,我们由 dp[p2][j] 即可求出最优路线(具体见图)
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; typedef long long ll; int t,w,d[4],inque[4][60001]; ll k,dp[4][60001]; struct que{int p,m;}; queue<que> q; int main(){ scanf("%d",&t); while(t--){ scanf("%lld%d%d%d%d",&k,&d[0],&d[1],&d[2],&d[3]); w=2*min(d[0],d[1]); memset(dp,0x7f,sizeof(dp)); dp[1][0]=0; q.push((que){1,0}); inque[1][0]=1; while(!q.empty()){ int p=q.front().p; int m=q.front().m; int nxt=(p+1)%4; int pre=(p+3)%4; q.pop(); inque[p][m]=false; if(dp[p][m]+d[p]<dp[nxt][(m+d[p])%w]){ dp[nxt][(m+d[p])%w]=dp[p][m]+d[p]; if(!inque[nxt][(m+d[p])%w]){ q.push((que){nxt,(m+d[p])%w}); inque[nxt][(m+d[p])%w]=1; } } if(dp[p][m]+d[pre]<dp[pre][(m+d[pre])%w]){ dp[pre][(m+d[pre])%w]=dp[p][m]+d[pre]; if(!inque[pre][(m+d[pre])%w]){ q.push((que){pre,(m+d[pre])%w}); inque[pre][(m+d[pre])%w]=1; } } } while(dp[1][k%w]>k)k++; printf("%lld\n",k); } return 0; }