c - 在这个 DP 问题中左移的目的是什么?
问题描述
我最近在网上找到了这个问题的解决方案(来自 leetcode.com):
在“100 游戏”中,两名玩家轮流将 1..10 中的任何整数加到运行总数中。最先使总分达到或超过 100 的玩家获胜。如果我们改变游戏规则让玩家不能重复使用整数会怎样?例如,两名玩家可能会轮流从 1..15 的公共号码池中抽奖,直到总数 >= 100。
给定一个 integermaxChoosableInteger
和另一个 integer desiredTotal
,确定第一个移动的玩家是否可以强制获胜,假设两个玩家都发挥最佳。您始终可以假设maxChoosableInteger
不会大于 20 并且desiredTotal
不会大于 300。
int canWin(int *dp, int v, int m, int total) {
int i, hewins;
if (dp[v] != -1) return dp[v]; // already resolved
for (i = m; i >= 1; i --) {
if (v & (1 << (i - 1))) continue;
v |= (1 << (i - 1));
if (i >= total) {
dp[v] = 1; // resolve to win
return 1;
}
hewins = canWin(dp, v, m, total - i);
if (!hewins) return 1;
v &= ~(1 << (i - 1)); // not able to resolve, try with different number
}
dp[v] = 0; // resolve to loose
return 0;
}
bool canIWin(int maxChoosableInteger, int desiredTotal) {
int w, dp[1 << 20];
// none can win
if (maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal) return false;
//dp = calloc((1 << (maxChoosableInteger)), sizeof(int));
//assert(dp);
memset(dp, -1, (1 << (maxChoosableInteger)) * sizeof(int));
w = canWin(dp, 0, maxChoosableInteger, desiredTotal);
//free(dp);
return w;
}
在上面的代码中,有 3 个主要的实例使用了左移。
memset(dp, -1, (1 << (maxChoosableInteger)) * sizeof(int));
和
dp[1 << 20]
以及所有类似的实例:
if (v & (1 << (i - 1))) continue;
问题的解决方案是递归的,这个问题是递归解决的,但首先我不确定为什么需要这么大的数组来存储所有递归调用。每个输入最多应该有 20 次递归调用(因为canIWin(maxinteger, total) = canIWin(maxInteger - 1), total)
OR
canIWin((maxInteger - 2), total)
OR
...... canIWin(1, total)
(由于逻辑原因,它实际上不能达到 1,但这只是让想法得以理解)
任何解释为什么使用这种左移将不胜感激。
解决方案
推荐阅读
- javascript - 我的幻灯片不适用于本地主机中的节点
- c# - 从已发布的 WPF 连接数据库?
- php - 电子邮件 wordpress E_Errors
- python-3.x - 熊猫将唯一值打印为字符串
- openvino - 使用 OpenVino 进行 C-API 异步批量推理
- python-3.x - 如何 One Hot 对 pandas 中的混合字符串和数字单元格值进行编码?
- cognos - 有没有办法使用 Cognos SDK 来操作 BIBusTKServer?
- html - 在 HTML 属性中使用车把的默认转义是否安全
- javascript - 如何从 CDN 加载 React.Component 并渲染到另一个 React.Component
- c# - 在你已经实现了许多类之后在 C# 接口中添加一个新属性