硬币排成线关注问题
-
描述
ENG
有
n
个硬币排成一条线。两个参赛者轮流从右边依次拿走 1 或 2 个硬币,直到没有硬币为止。拿到最后一枚硬币的人获胜。请判定 先手玩家 必胜还是必败?
若必胜, 返回
true
, 否则返回false
.您在真实的面试中是否遇到过这个题? 是
题目纠错
样例
样例 1:
输入: 1 输出: true
样例 2:
输入: 4 输出: true 解释: 先手玩家第一轮拿走一个硬币, 此时还剩三个. 这时无论后手玩家拿一个还是两个, 下一次先手玩家都可以把剩下的硬币拿完.
挑战
O(1) 时间复杂度且O(1) 存储。
-
原理1:
-
本题一个人可以拿1个或者2个,如果输入的n符合条件,为了确保第一个人拿一定能够赢,则对拿法一定要有要求:第一个人在第一次拿之后一定要保证剩下的物品数量为3的倍数,接下来无论第二个人怎么拿,第一个人还是能把剩下的物品数量控制在3的倍数,因此第一个人一定能够确保自己拿到最后一枚硬币并获胜。因此对于输入的数n,是3或者3的倍数个 先手必败,其它情况选手必胜。
-
代码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 return n%3!=0; } }
原理2:
-
博弈:
-
一个状态是先手必胜状态 当且仅当 它的子状态中存在一个 先手必败状态
状态f[i]: 还有i个硬币时 是否是先手必胜
状态转移: f[i] = f[i - 1] == false || dp[i - 2] == false;
初始条件: f[1] = f[2] = true;
计算顺序: 从小到大
返回: f[n]
可以用滚动数组优化 -
代码2:
-
public class Solution { /** * @param n: An integer * @return: A boolean which equals to true if the first player will win */ public boolean firstPlayer(int n) { // write your code here boolean []f = new boolean[n+2]; f[0] = false; f[1] = true; for(int i=2; i<=n; ++i) f[i] = !f[i-1] || !f[i-2]; return f[n]; } }