♐ CSP_J 组真题
2007年
3. 守望者的逃离

线上OJ:

一本通:1940:【07NOIP普及组】守望者的逃离 (opens in a new tab)
AcWing:431. 守望者的逃离 (opens in a new tab)
洛谷:P1095 [NOIP2007 普及组] 守望者的逃离 (opens in a new tab)

核心思想:

1、闪烁耗费1秒,可以增加距离60米,但是恢复10点魔法值还需要2.5秒,所以闪烁的平均速度为 60m/3.5s = 17.14m/s > 跑步的速度17m/s。所以在时间充足的情况下,闪烁比跑步距离更远。
2、但是如果最后剩余的时间不足以支持一次闪烁,则跑步能增加距离。
3、所以,先按照最快的闪烁,计算每一秒钟能出现的位置(闪烁的秒,位置+60;不闪烁的秒,位置不变);最后用跑步来修正每一次等待过程中的距离(等待的秒,可以用跑步+17来更新距离)

动态规划
#include <bits/stdc++.h>
using namespace std;
int m, s, t, dp[300005];
 
// 恢复10点需要2.5秒,闪烁需要1秒,所以闪烁的平均速度为 60/3.5 = 17.14m/s  > 跑步的速度17m/s
// 所以在时间充足的情况下,闪烁比跑步距离更远。
// 先按照最快的闪烁,计算每一秒钟可能出现的位置;最后用跑步来修正每一次等待过程中的距离
int main()
{
  scanf("%d %d %d", &m, &s, &t);
  for(int i = 1; i <= t; i++)
  {
      if(m >= 10)
      {
          dp[i] = dp[i-1]+60;   // 如果魔法值够,则跳跃60米
          m -= 10;
      }
      else  // 如果魔法值不够,则原地等待,恢复魔法值
      {
          dp[i] = dp[i-1];
          m += 4;
      }
  }
  for(int i = 1; i <= t; i++)
  {
      dp[i] = max(dp[i], dp[i-1] + 17);   // 对于等待的秒,用跑步来修正每一次等待过程中的距离
      if(dp[i] >= s)
      {
          printf("Yes\n%d", i);
          return 0;
      }
  }
  printf("No\n%d", dp[t]);  // 如果跑不完,则输出最后t时刻的距离即可
  return 0;
}