我们正在玩一个猜数游戏,游戏规则如下:
我从 1 到 n 之间选择一个数字,你来猜我选了哪个数字。
每次你猜错了,我都会告诉你,我选的数字比你的大了或者小了。
然而,当你猜了数字 x 并且猜错了的时候,你需要支付金额为 x 的现金。直到你猜到我选的数字,你才算赢得了这个游戏。
示例:
n = 10, 我选择了8. 第一轮: 你猜我选择的数字是5,我会告诉你,我的数字更大一些,然后你需要支付5块。 第二轮: 你猜是7,我告诉你,我的数字更大一些,你支付7块。 第三轮: 你猜是9,我告诉你,我的数字更小一些,你支付9块。 游戏结束。8 就是我选的数字。 你最终要支付 5 + 7 + 9 = 21 块钱。
给定一个 n ≥ 1,计算你至少需要拥有多少现金才能确保你能赢得这个游戏。
思路:这道题我们来看规律。
1:假如n=1或者n=0,那么cost=0
2:假如n=1,2,那么我们猜较小的误差1,根据反馈可以得到正确答案,cost=1
3:假如n=1,2,3,那么猜2,根据反馈也可以得到正确答案,cost=2
到目前为止还没看出来规律,因为前3种是我们的边界条件(第三种其实可以分解为更详细的子问题,不算边界条件,但是不明显,所以暂不分解)
4:假如n=1,2,3,4,由于我们不知道哪种好,所以只能逐个猜:
猜k=1,对于左边而言,cost=0,对于右边[2,3,4],根据上面的分析,cost=3。综合来看cost=k+max(cost(left)+cost(right))=1+max(0,3)=4
猜k=2,对于左边1,cost=0,对于右边3,4,cost=3,综合cost=2+max(0,3)=5
猜k=3,对于左边1,2,cost=1,对于右边4,cost=0,综合cost=3+max(1,0)=4
猜k=4,对于左边1,2,3,cost=2,对于右边无,cost=0,综合cost=4+max(2,0)=2
取k=1,2,3,4中对应最小的cost总和是4。对于n=1,2,3,4,cost最小是4。
所以我们用memo[i][j]表示[i,j]范围内的最小的cost,用递归得到如下参考代码:
class Solution {
public:
int helpCore(int start,int end, vector<vector<int>>& memo) {
if (start >= end) {
return 0;
}
if (start == (end - 1)) {
memo[start][end] = start;
return memo[start][end];
}
if (memo[start][end] > 0) {
return memo[start][end];
}
int res = INT_MAX;
for (int k = start+1; k < end; k++) {
int t = k + max(helpCore(start, k - 1, memo), helpCore(k + 1, end, memo));
res = min(res, t);
}
memo[start][end] = res;
return memo[start][end];
}
int getMoneyAmount(int n) {
vector<vector<int>> memo(n + 1, vector<int>(n + 1, 0));
return helpCore(1, n, memo);
}
};