375. Guess Number Higher or Lower II
一、问题描述
We are playing the Guess Game. The game is as follows:
I pick a number from 1 to n. You have to guess which number I picked.
Every time you guess wrong, I’ll tell you whether the number I picked is higher or lower.
However, when you guess a particular number x, and you guess wrong, you pay $x. You win the game when you guess the number I picked.
二、输入输出
Example:
n = 10, I pick 8.
First round: You guess 5, I tell you that it's higher. You pay $5.
Second round: You guess 7, I tell you that it's higher. You pay $7.
Third round: You guess 9, I tell you that it's lower. You pay $9.
Game over. 8 is the number I picked.
You end up paying $5 + $7 + $9 = $21.
Given a particular n ≥ 1, find out how much money you need to have to guarantee a win.
三、解题思路
动态规划
Recursive and Memorize
- 题目的意思是你给出一个最小的花费和,使得无论选择是多少都有一种选择的方案让我可以赢。这里说可以赢,并不是每次你乱走都能赢,而是按照最优策略走才能赢。
- 动态规划需要分类考虑的永远是你
第一步需要怎么走
根据你走的这一步确定出子问题,然后假设子问题已经解决,merge子问题得到原问题的解。 - 对于这种有对抗性质或者需要等待对方的动作才能做出决策的情况,你只能考虑自己怎么走。也就是说你分类里面的第一步只能是你自己的动作,像这道题就是
第一步你要猜哪个值
。我肯定不知道第一步应该猜哪个值对吧?没关系,那就全部猜一遍,取这里面最小的值,意思就是我用这个花费,无论你第一步怎么猜都有一个猜法保证我能赢。这也是动态规划最难的地方。 - 那么我猜之后,跟对手猜的值到底有什么关系那?好,我们这样想,假设对手pick了一个值,我们不知道,现在我们猜测了一个k。为了保证我们有一种策略能赢,那么就要考虑最坏的情况!什么样的情况是最坏的那,就是我们猜的k没有猜对。没有猜对也有两种情况,那么哪一种情况对我们来说是最坏的那? 无非有两种情况,我们只要取更坏的一次就行了,就是
d(i,j) = max{ k + d(i, k -1),k + d(k+1, j)}
其中d(i,j)是在[i,j]中保证能赢的最低花费,k是我这一次猜测的值(或者叫第一次猜测的值)。original problem is d(1,n) - 迭代的base case是 if(i==j) d(i,j) = 0
- 动态规划的第一次动作或第一次决策其实就是你的每一次决策或者动作。因为程序是递归调用的,子问题与原问题是同类型的问题,所以对于原问题来说的第n次决策,对于子问题来说都是第一次决策。
class Solution {
public:
map<vector<int>, int> mem;
int DP(int i, int j){
if(i >= j) return 0;
vector<int> line = {i,j};
if(mem.find(line) != mem.end()) return mem[line];
int minRet = INT_MAX;
for (int k = i; k <= j; ++k) {
int tmp = k + max(DP(i,k-1), DP(k+1, j));
minRet = min(minRet, tmp);
}
mem[line] = minRet;
return minRet;
}
int getMoneyAmount(int n) {
return DP(1,n);
}
};
Bottom Up的方法,暂时贴出来供参考
public class Solution {
public int getMoneyAmount(int n) {
int[][] table = new int[n+1][n+1];
for(int j=2; j<=n; j++){
for(int i=j-1; i>0; i--){
int globalMin = Integer.MAX_VALUE;
for(int k=i+1; k<j; k++){
int localMax = k + Math.max(table[i][k-1], table[k+1][j]);
globalMin = Math.min(globalMin, localMax);
}
table[i][j] = i+1==j?i:globalMin;
}
}
return table[1][n];
}
}