题意
猜数字游戏,给定一个n,假设我从1到n里面选择了一个数作为我的target,如果我这次猜的x并且猜错了, 那么需要付出代价x,能够猜出1到n里面任意一个数的最小代价。
思路
这道题和之前的Guess Number Higher or Lower的不同之处在于:之前一道题是规定了每次猜中间一个数,因此值二分即可。这道题是每次可以从[L, R]里面猜任意一个数,但是要求总的代价最小。
状态表示:d[i][j],猜出区间[i, j]里面任意一个数需要的最小代价。
现在,我们可以这样考虑,当我们要猜的数在[i, j]中时,我们需要做的就是从[i, j]里面猜一个数,假设猜的是k。
- 如果target更大,那么我们就需要在[k + 1, j]里面猜,并且付出代价k。
- 如果target更小,那么我们就需要在[i, k - 1]里面猜,并且付出代价k。
- 如果targt == k,那么我们只需要付出代价k。
因为要确保我们总能赢,因此可以得到我们的转移方程:
d[i][j]=min{max(d[i][k−1],d[k+1][j])+k|i≤k≤j}
时间复杂度:O(n3)
空间复杂度:O(n2)
代码
const int maxn = 1005;
int d[maxn][maxn];
class Solution {
public:
int dfs(int i, int j) {
if (d[i][j] != -1) return d[i][j];
if (i >= j) return d[i][j] = 0;
if (j == i + 1) return d[i][j] = i;
d[i][j] = 0x3e3e3e3e;
for (int k = i; k <= j; k++) {
d[i][j] = min(d[i][j], max(dfs(i, k - 1), dfs(k + 1, j)) + k);
}
return d[i][j];
}
int getMoneyAmount(int n) {
memset(d, -1, sizeof(d));
return dfs(1, n);
}
};