题目描述:
有 n 个不同价值的硬币排成一条线。两个参赛者轮流从左边依次拿走 1 或 2 个硬币,直到没有硬币为止。计算两个人分别拿到的硬币总价值,价值高的人获胜。
请判定第一个玩家是输还是赢?
样例:
给定数组 A = [1,2,2], 返回 true.
给定数组 A = [1,2,4], 返回 false.
动态规划的解法:
①建立数组DP[i]表示拿第i个硬币到第n个硬币所能获得的最大利润
②对于玩家拿到第i个硬币时,玩家拿硬币仍是从左往右拿。因此他有两种选择,仅拿第i个硬币或者拿第i和i+1个硬币,这两种算法均会产生一个价值作为DP[i]的值;因为我们是从后往前进行动态规划
③第二个玩家在我们做出选择后也会对第一个玩家的利益进行限制,例如当第一个玩家拿第i个硬币时,他可以拿第i+1或第i+1、i+2个硬币,但是他会对结果进行判断,保证第一个玩家的获利达到最小,即选择DP[i + 2]和DP[i + 3]中的最小值来决定自己是拿一个还是拿两个
综上,动态规划表达式为DP[i] = max{nums[i] + min{DP[i + 2], DP[i + 3]}, num[i] + num[i + 1] + min{DP[i + 3], DP[i + 4]}};
n = len(values)
if n == 0:
return False
if n < 3:
return True
DP = [0] * (1 + n)
DP[n] = 0
DP[n - 1] = values[n - 1]
DP[n - 2] = values[n - 1] + values[n - 2]
DP[n - 3] = values[n - 2] + values[n - 3]
for i in range(n - 4, -1, -1):
DP[i] = values[i] + min(DP[i + 2], DP[i + 3])
DP[i] = max(DP[i], values[i] + values[i + 1] + min(DP[i + 3], DP[i + 4]))
summ = sum(values)
return DP[0] * 2 > summ
上面算法时间复杂度O(n),空间复杂度O(n)
利用滚动数组,可以使空间复杂度降到O(1)
if not values:
return False
if len(values) <= 2:
return True
n = len(values)
# dynamic programming
f = [0] * 3
prefix_sum = [0] * 3
f[(n - 1) % 3] = prefix_sum[(n - 1) % 3] = values[n - 1]
# traverse values in reverse order from n-1 to 0
for i in range(n - 2, -1, -1):
prefix_sum[i % 3] = prefix_sum[(i + 1) % 3] + values[i]
f[i % 3] = max(
values[i] + prefix_sum[(i + 1) % 3] - f[(i + 1) % 3],
values[i] + values[i + 1] + prefix_sum[(i + 2) % 3] - f[(i + 2) % 3],
)
return f[0] > prefix_sum[0] - f[0]