375. 猜数字大小 II

我们正在玩一个猜数游戏,游戏规则如下:

我从 到 n 之间选择一个数字,你来猜我选了哪个数字。

每次你猜错了,我都会告诉你,我选的数字比你的大了或者小了。

然而,当你猜了数字 x 并且猜错了的时候,你需要支付金额为 x 的现金。直到你猜到我选的数字,你才算赢得了这个游戏。

示例:

n = 10, 我选择了8.

第一轮: 你猜我选择的数字是5,我会告诉你,我的数字更大一些,然后你需要支付5块。
第二轮: 你猜是7,我告诉你,我的数字更大一些,你支付7块。
第三轮: 你猜是9,我告诉你,我的数字更小一些,你支付9块。

游戏结束。8 就是我选的数字。

你最终要支付 5 + 7 + 9 = 21 块钱。

给定 n ≥ 1,计算你至少需要拥有多少现金才能确保你能赢得这个游戏。

思路:这是一道Minimax算法 又名极小化极大算法问题。局部最大值,全局最小值。举例说明:

1、当n = 1时,显然cost = 0

2、当n = 2时,即[1, 2],显然cost=1

3、当n = 3时,即[1, 2, 3],只需要猜中间数字2就能知道结果,因为比2大的只有一个,比2小的也只有一个,所以cost=2

4、当n = 4时,即[1, 2, 3, 4] 。四个数字显然不能一眼看出最少cost,那就一个一个来计算吧。

  • 如果先猜1,如果猜中,则cost=0,否则数字在[2-4]之间,这时需花费cost = 1 + Cost(2, 4) = 1 + 3 = 4
  • 如果先猜2,则cost = 2 + Cost(3,4) = 2 + 3 = 5
  • 如果先猜3,则cost = 3 + Cost(1,2) = 3 + 1 = 4
  • 如果先猜4,则cost = 4 + Cost(1,3) = 4 + 2 = 6
  • ==综上所列==,当有4个数字时,至少要带¥4才能确保能赢得这个游戏。

5、当n = 5时,即[1, 2, 3, 4, 5],同样可以挨个计算每个数字,最终得到最小cost

综上所述,当给定数字n时,可以从1开始,计算全部至少花费情况,最后取最小花费。

因此有[i,j]区间内的局部最大值 local_max = k + max(cost(i, k-1), cost(k+1, j)),取最大值的原因是不知道实际数字是比k大,还是比k小,一定要考虑到所有情况,按最坏情况处理。

最后对每一k得到的local_max取最小值,得到[i,j]区间至少需要多少钱赢得游戏。

注意k是我们可控的,实际数字是我们不可控的。

实际书写代码中,采用记忆数组dp,dp[i][j]表示[i,j]区间至少需要多少钱赢得游戏,把已经得到的数保存下来,减少重复计算。

class Solution {
public:
    int helper(int s, int e, vector<vector<int> > &dp){
        if(s>=e) return 0; 
        if(dp[s][e]) return dp[s][e]; //之前已经求出了dp[s][e]
        int re=INT_MAX;
        for(int i=s; i<=e; ++i){
            re=min(re, i + max(helper(s,i-1, dp), helper(i+1, e, dp)));          
        }
        dp[s][e]=re;
        return re;
    }

    int getMoneyAmount(int n) {
        vector<vector<int> >dp(n+1, vector<int>(n+1, 0));
        return helper(1, n, dp);
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值