题目:
我们正在玩一个猜数游戏,游戏规则如下: 我从 1 到 n 之间选择一个数字。 你来猜我选了哪个数字。 如果你猜到正确的数字,就会 赢得游戏 。 如果你猜错了,那么我会告诉你,我选的数字比你的 更大或者更小 ,并且你需要继续猜数。 每当你猜了数字 x 并且猜错了的时候,你需要支付金额为 x 的现金。如果你花光了钱,就会 输掉游戏 。 给你一个特定的数字 n ,返回能够 确保你获胜 的最小现金数,不管我选择那个数字
思路
区间动态规划,[n][n] 只有一个数字那么,一次即可猜中,所以是0
// 区间dp
//dp[i][j]:在[i,j]之间猜数最少花费
//枚举先猜k,则花费k,剩下区间[i,k-1]和[k+1,j],因为会告诉你大了或小了,所以肯定只选择一个,又因为我们要保证猜对,所以取max{dp[i][k-1],dp[k+1][j]} ,即有:
//dp[i][j]=min(dp[i][j], k+max(dp[i][k-1],dp[k+1][j]));
// 假设这个[i,j]区间为[1,2],我猜的数字k = 1;不对的情况为 i, k -1 和 k + 1, j.[1][0]和[2][2]
// [1][0],只有一个数所以0,[2][2],只有一个数所以0,但是我们猜的数字1 是要钱的
// 所以 [i][j] 的值等于min(dp[i][j],k + max([i][k-1],[k+1][j])) 为 1;
// 扩大区间[1,3],我猜的数字k = 1 ,min(dp[1][3],1 + max([1][0],[2][3])) = 3;
// 扩大区间[1,3],我猜的数字k = 2 ,min(dp[1][3],2 + max([1][1],[3][3])) = 2;
// 扩大区间[1,3],我猜的数字k = 3 ,min(dp[1][3],3 + max([1][0],[2][3])) = 5;
// 最小的是猜2;
int[][] dp = new int[n + 1][n + 1];
for(int j = 2; j <= n; j++){ // [i,j] 不会选择0,所以从 区间[1][2]开始
for (int i = j - 1; i >= 1; i--) { //区间在 [i , j];如[2,3]表示两个数,2和3,那么稳赢就是猜2 最少花费2块钱
dp[i][j] = n * n;//赋值为最大值
for (int k = i; k < j; k++) { // 在 [i,j]这个区间中选择一个k,
dp[i][j] = Math.min(dp[i][j],k + Math.max(dp[i][k - 1],dp[k + 1][j]));
}
}
}
return dp[1][n];//返回[1,n]区间中,花钱最少的值