动态规划-硬币排成线
- 1 描述
- 2 样例
- 2.1 样例 1:
- 2.2 样例 2:
- 2.3 样例 3:
- 3 算法解题思路及实现
- 3.1 算法解题分析
- 3.1.1 确定状态
- 3.1.2 转移方程
- 3.1.3 初始条件和边界情况
- 3.1.4 计算顺序
- 3.2 算法实现
- 3.2.1 动态规划常规实现
- 3.2.2 动态规划滚动数组
该题是lintcode的第394题, https://www.lintcode.com/problem/394/,解题思路参考九章侯老师给的建议。
1 描述
有 n 个硬币排成一条线。两个参赛者轮流从右边依次拿走 1 或 2 个硬币,直到没有硬币为止。拿到最后一枚硬币的人获胜。
请判定 先手玩家 必胜还是必败?
若必胜, 返回 true, 否则返回 false.
Lintcode超级VIP年卡 618预售 享买一年送一年
微信加【jiuzhang1104】备注【VIP】即可参加
2 样例
2.1 样例 1:
输入: 1
输出: true
2.2 样例 2:
输入: 4
输出: true
解释:
先手玩家第一轮拿走一个硬币, 此时还剩三个.
这时无论后手玩家拿一个还是两个, 下一次先手玩家都可以把剩下的硬币拿完.
2.3 样例 3:
输入: 5
输出: true
解释:
先手玩家第一轮拿走两枚硬币, 此时还剩三个.
这时无论后手玩家拿一个还是两个, 下一次先手玩家都可以把剩下的硬币拿完.
3 算法解题思路及实现
3.1 算法解题分析
3.1.1 确定状态
这是一个博弈型动态规划类算法题,这中类型的算法解题分析不是从最后一步分析,而是从第一步分析,原因是随着硬币的减少,问题越来越简单。
假设两名选手分别为A和B,A为硬币为N时的先手,而当A拿走一或则两枚硬币之后,B则成为剩余硬币的先手,因此A要保证在自己拿走1或者2枚硬币的时候至少有一种情况是可以赢的。
子问题就转换为:
面对N枚硬币,A作为先手是不是必胜,
则需要知道面对N - 1和N - 2枚硬币B作为先手是不是必胜,
状态:设f[i]表示面对i个石子,是否先手必胜f[i] = True/False
3.1.2 转移方程
状态:设f[i]表示面对i个石子,是否先手必胜f[i] = True/False
f[i] = (f[i-1] == FALSE OR f[i-2] == FALSE)
3.1.3 初始条件和边界情况
- 设f[i]表示面对i个石子,是否先手必胜f[i] = True/False
- f[i] = (f[i-1] == FALSE OR f[i-2] == FALSE)
- f[0] = FALSE 面对0枚硬币,先手必败
- f[1] = f[2] = True, 面对1枚或2枚硬币,先手必胜
3.1.4 计算顺序
- f[0], f[1], f[2], …, f[N]
- 如果f[N] = true,则先手必胜,否则先手必败
- 时间负责度为O(N)
- 空间复杂度为:O(N)
3.2 算法实现
3.2.1 动态规划常规实现
public class Solution {
/**
* @param n: An integer
* @return: A boolean which equals to true if the first player will win
*/
public boolean firstWillWin(int n) {
// write your code here
if (n <= 0) {
return false;
}
if (n <= 2) {
return true;
}
boolean [] f = new boolean[n + 1];
f[0] = false;
f[1] = true;
for (int i = 2; i <= n; i++) {
f[i] = (!f[i - 1] || !f[i - 2]);
}
return f[n];
}
}
3.2.2 动态规划滚动数组
public class Solution {
/**
* @param n: An integer
* @return: A boolean which equals to true if the first player will win
*/
public boolean firstWillWin(int n) {
// write your code here
if (n <= 0) {
return false;
}
if (n <= 2) {
return true;
}
boolean [] f = new boolean[2];
f[0] = false;
f[1] = true;
for (int i = 2; i <= n; i++) {
f[i % 2] = !(f[(i -1) % 2] && f[(i - 2) % 2]);
}
return f[n % 2];
}
}