很明显的动态规划,设置 dp[n],dp[i] 表示调到数字 i 所需的最少步数。这道题的状态转移方程很简单:
dp[i] = min(dp[i - 1], dp[(i - k) % n]) + 1
不过,这道题难的地方就在于,dp 的初始化比较难想到,有一点比较细。正确的初始化方案如下:
// 从 0 位置开始, 逐次往后跳 1 步
for i in [0, n - 1]:
dp[i] = i
// 从 k 位置开始, 逐次往后跳 k 步, 根据数学关系可推导出最多跳 n * k / gcd(n, k)
// 步就可以覆盖 [0, n - 1]所有数字
step = 1 // 记录步数
temp = k // 记录位置
for i in [0, n * k / gcd(n, k)]:
dp[i] = min(dp[i], step)
temp = (temp + k) % n
step += 1
然而,这么做还是不行,会超时,因为如果 n, k 互质,n * k / gcd(n, k) 相当于 n * k,这个数字可能会非常大。
解决的方案也很简单,我们可以知道最终 dp[i] 最大不会超过 n - 1,也就是说任意一个数字最多只需 n - 1 步就可以到达。因此,将第二部的初始化代码修改如下:
for i in [0, min(n - 1, n * k / gcd(n, k)]:
dp[i] = min(dp[i], step)
temp = (temp + k) % n
step += 1
Python 代码如下:
import sys
def gcd(x, y):
if x > y:
x, y =y, x
r = y % x
while r:
x, y = r, x
r = y % x
return x
n, k = list(map(int, sys.stdin.readline().strip().split()))
dp = [_ for _ in range(n)]
step, temp = 1, k
for i in range(min(n, n * k // gcd(k, n))):
dp[temp] = min(dp[temp], step)
step += 1
temp = (temp + k) % n
for i in range(n):
dp[i] = min(dp[i], dp[i - 1] + 1, dp[i - k] + 1)
print(max(dp))