【魅力算法】"1025. 除数博弈" 动态规划、位运算实现 |刷题打卡

167 阅读1分钟

题目描述:

爱丽丝和鲍勃一起玩游戏,他们轮流行动。爱丽丝先手开局。

最初,黑板上有一个数字 N 。在每个玩家的回合,玩家需要执行以下操作:

选出任一 x,满足 0 < x < N 且 N % x == 0 。
用 N - x 替换黑板上的数字 N 。
如果玩家无法执行这些操作,就会输掉游戏。

只有在爱丽丝在游戏中取得胜利时才返回 True,否则返回 False。假设两个玩家都以最佳状态参与游戏。

 

示例 1:

输入:2
输出:true
解释:爱丽丝选择 1,鲍勃无法进行操作。
示例 2:

输入:3
输出:false
解释:爱丽丝选择 1,鲍勃也选择 1,然后爱丽丝无法进行操作。
 

提示:

1 <= N <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/divisor-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路-动态规划

  • 条件:1 <= N <= 1000
  • 定义:dp[i]表示N取i时,当前游戏选手是否取胜;
    • 先手爱丽丝:dp[1]当N==1时,爱丽丝必输;dp[2]当N==2时,爱丽丝必胜;
    • 同理后手鲍勃:如果拿到dp[i]==false,则!dp[i]为爱丽丝是否取胜的结果;
  • 遍历:在1-N之间找一个数x,且N%x==0;
  • 状态转移方程:d[i] =(i % x === 0 && !dp[i - x])

代码

/**
 * @param {number} N
 * @return {boolean}
 */
var divisorGame = function(N) {
let dp=[true,false];
for(let i=1;i<=N;i++){
    for(let x=i;x-->1;){
        if(dp[i]=i%x===0&!dp[i-x]){
            break;
        }
    }
}
return dp[N]
};

优化:减少循环:

for(var x = i; x-- > 1;) if(dp[i] = (i % x === 0 & !dp[i - x])) break

由以上代码可知:

  • x从i到1,在当前循环层中,dp[i]的值每次都被覆盖,最终值都为x=1时的值;
  • dp[i]=(i%1===0&!dp[i-1]),i%1===0为true;则:
    • dp[i]=!dp[i-1];
  • 只要取x=1,就可以让本局结果和上一局相反;即:赢得本局博弈;
  • 1可整除任何数,Alice和Bob每局都可以拿1,改变结果。双方都是最佳状态
    • Alice必败时,拿1,让Bob必败
    • Bob必败时,拿1,让Alice必败
/**
 * @param {number} N
 * @return {boolean}
 */
var divisorGame = function(N) {
let dp = [true];
for(var i = 1; i <= N; i++) dp[i] = !dp[i - 1]
    return dp[N]
};

优化:每一局的状态,只与上一局有关,降低dp维度:

/**
 * @param {number} N
 * @return {boolean}
 */
var divisorGame = function(N) {
let dp = true;
for(var i = 1; i <= N; i++) dp=!dp
    return dp[N]
};

解题思路-位运算

  • 用位运算判断奇偶性,& 1二进制末位是1奇数,0偶数,
  • ^ 1异或取反;
  • boolean:true/1;false/0
/**
 * @param {number} N
 * @return {boolean}
 */
var divisorGame = function(N) {
 return N&1^1 
};