原题目:
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.
中文大意
有一个猜数字的游戏,给定一个范围n,去猜一个[1,n]的目标数字,每当你猜一个数字i,游戏会返回你猜的数字i比n大还是比n小,并且要罚i这么多的钱,游戏直到你猜对数字才会停止(猜对的话不需要罚钱)。问:如果我要保证赢的话,最少需要罚多少钱?
题解
class Solution {
public:
int getMoneyAmount(int n) {
vector<vector<int> > dp(n+1,vector<int>(n+1,0));
for(int l = 1; l <= n;l++)
{
for(int i = 1; i <= n-l ;i++)
{
if(l == 1) dp[i][i+l] = i;
else if(l == 2) dp[i][i+l] = i+1;
else
{
int mincost = min(i+dp[i+1][i+l],i+l+dp[i][i+l-1]);
for(int j = i+1; j < i+l ;j++)
mincost = min(mincost,j+max(dp[i][j-1],dp[j+1][i+l]));
dp[i][i+l] = mincost;
}
}
}
return dp[1][n];
}
};
解析:
这道题仍然是用动态规划来解决。定义f[i][j]为[i,j]之间要保证猜中所花费的最少钱,状态转移方程为:f[i][j] = min{k + max{f[i][k-1] , f[k+1][j]} }
直观来讲,就是如果要求[i,j]之间保证能猜中的最少钱,那么问题可以分解为:
- 猜k但是不中(因为要求至少),所以f[i][j]要加上k
- 这个时候,目标数字有可能大于k也有可能小于k,这个时候,f[i][j]需要加上f[i][k-1],f[k+1][j]的最大值(因为要保证够钱能猜中)
有了状态转移方程以后,还有一个关键是转移的方向(或者说是迭代的方向),由于状态之间的依赖关系:
- 例如f[1][3],即(f[1][1+2])需要依赖f[1][1],f[1][2],f[2][2],f[2][3],即f[1][1+0],f[1][1+1],f[2][2+0],f[2][2+1]
- 定义跨度为j-i的值,意味着,要从跨度入手来进行遍历:要先计算f[i][i+0],然后再算f[i][i+1]……
根据这种思路,算出算法的空间复杂度为O(n^2),时间复杂度为O(n^3)