375. Guess Number Higher or Lower II

来源:互联网 发布:c语言中x a 3 b a 编辑:程序博客网 时间:2024/05/15 17:37

一、题目大意

我们在玩一个猜数字的游戏,游戏规则如下:
我从1到n选择一个数字,你来猜我选择的数字。
每次你猜错,我都会告诉真实值是偏大还是偏小。
然而,当你猜了一个数字x,并且猜错的时候,你必须支付$x给我,当你猜到我选择的数字时,你就可以赢得比赛。
例如:
n=10,我选择了8
第一轮:你猜5,我告诉你真实值大于该值,你支付$5;
第二轮:你猜7,我告诉你真实值大于该值,你支付$7;
第三轮:你猜9,我告诉你真实值小于该值,你支付$9;
游戏结束,我选择的数字是8 。
你最终支付了$5+$7+$9=$21。
给定一个值n1,求为了保证赢得比赛,你必须有多少钱。

二、编程思路

2.1、2.2节为不成功的编程思路,关心答案的同学可以直接转到2.3即可。

2.1 初刻拍案惊奇

乍一看此题可以使用二分法求解。
n=1,{1},ans=0;
n=2,{1,2},ans=1;
n=3,{1,2,3},ans=2;
n=4,{1,2,3,4},ans=min{2+3,3+1}=4;

似乎没问题,令f(x,y)表示猜[x,y]范围内所需要花的钱,med=x+y2,若med为整数,则先猜med;若med不是整数,则猜medf=floor(med)medc=ceil(med)两种情况,取其中情况较小的。
总结其递推公式如下:

  • f(x,y)=0;
  • f(x,y)=x,  y=x+1;
  • f(x,y)=med+f(x,med1)+f(med+1,y),  medc==medf
  • f(x,y)=min{medc+f(x,medc1)+f(medc+1,y),medf+f(x,medf1)+f(medf+1,y)},  medc!=medf

但是当n==5时,根据二分法求出来的是:n=5,{1,2,3,4,5},f(1,5)=3+4=7,而正确的结果应该是f(1,5)=2+4=6。
二分法宣告失败,这是因为二分法只能够找到猜的次数最少的,而不能够保证所猜的数字和最小。

2.2 二刻拍案惊奇

既然二分法不行,那就使用动态规划思路进行求解,继续找规律。
n=1,{1},ans=0;
n=2,{1,2},ans=min{1,2}=1;
n=3,{1,2,3},ans=min{1+2,2,3+1};枚举第一次分别猜1 2 3的情况。
n=4,{1,2,3,4},ans=min{1+{2,3,4}, 2+{3,4}+{1},3+{1,2}+{4}, 4+{1,2,3}}=4;

n,{1,2,3,,n},ans=MINi=ni=1{i+max({1,2,3,,i1}, {i+1,,n}},即分别枚举猜1n的情况,从而将问题分解为左右两个子序列的问题。即上式中的{1,2,3,,i1}{i+1,,n},然后分别求其需要的钱数即可。

至此,思路还没有出现问题。
经过初步观察似乎可以发现,右边序列需要的钱数{i+1,,n}?=()i+{1,,ni}于是总结出如下递推公式:

  • 令dp[i]表示数字范围为1…i之间时,为了保证猜中数字所需要的最少钱数,则
  • dp[0]=0;
  • dp[1]=0;
  • dp[2]=1;
  • dp[i]=MINk=ik=1(k+max{dp[k1],dp[ij]+((ij)<2?0:j)})

2.3 三刻拍案惊奇

以上思路中,右边序列需要的钱数{i+1,,n}?=()i+{1,,ni}属于根据直觉得出,与正确结果对比发现,当n12时,会出现错误。其实只有当子序列中只猜一次时,以上关系才成立。
继续使用动态规划思路,令dp[i][j]猜中[i,j]范围之内数字时所需要最少钱数。则递推公式很容易获得,
dp[i][j]=MINk=jk=i{k+max{dp[i][k1],dp[k+1][j]}}

三、代码设计

class Solution {  public:      int getMoneyAmount(int n) {          if(n ==0) return 0;          vector<vector<int> > dp(n+1, vector<int>(n+1, 0));        //注意此处的迭代方向          for(int i = n-1; i > 0; i--)          {              for(int j = i+1; j <=n; j++)              {                  int ans = 0xfffffff;                  for(int k = i; k <j; k++)                      ans = min(ans, k + max(dp[i][k-1], dp[k+1][j]));                  dp[i][j] = ans;              }          }          return dp[1][n];      }  };

参考博客

LeetCode题解
[LeetCode]Guess Number Higher or Lower II

0 0
原创粉丝点击