You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
3.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2021_11_4_week;
// 测试链接 : https://leetcode.com/problems/guess-number-higher-or-lower-ii/
public class Code02_GuessNumberHigherOrLowerII {
// 正确的数字在1~n之间
// 每次猜错,花费就是你猜的数字
// 返回:永远最倒霉的情况下,也能赢的胜利,所需要的最少钱数
public static int minGold(int n) {
return zuo(1, n);
}
// 目前锁定了正确的数字在L~R范围上除了这个范围都不可能了
// 返回,永远最倒霉的情况下,猜中这个数字,所需要的最少钱数
public static int zuo(int L, int R) {
if (L == R) {
return 0;
}
if (L == R - 1) {
return L;
}
int min = Math.min(L + zuo(L + 1, R), R + zuo(L, R - 1));
for (int i = L + 1; i < R; i++) {
min = Math.min(min, i + Math.max(zuo(L, i - 1), zuo(i + 1, R)));
}
return min;
}
public static int minGold2(int n) {
// L -> 1~n
// R -> 1~n
int[][] dp = new int[n + 1][n + 1];
// 因为初始化都是0所以dp的对角线不用填了
for (int i = 1; i < n; i++) {
dp[i][i + 1] = i;
}
for (int L = n - 2; L >= 1; L--) {
for (int R = L + 2; R <= n; R++) {
int min = Math.min(L + dp[L + 1][R], R + dp[L][R - 1]);
for (int i = L + 1; i < R; i++) {
min = Math.min(min, i + Math.max(dp[L][i - 1], dp[i + 1][R]));
}
dp[L][R] = min;
}
}
return dp[1][n];
}
// 暴力递归
public static int getMoneyAmount1(int n) {
return process1(1, n);
}
// 假设现在在L ~ R的范围上, 猜数字
// 返回:确保获胜的最小现金数,不管答案是哪个数字
// 注意:所谓的“确保获胜”,以及“不管答案是哪个数字”,意味着你每次永远面临猜错的最差情况!
public static int process1(int L, int R) {
// 说明L~R范围只剩下一个数字了那不用猜了获胜了
if (L == R) {
return 0;
}
// 说明L~R范围只剩下两个数字了
// 比如: 5 6
// 假设永远会遇到最差情况,
// 那么当然猜5因为最差情况下也只需要耗费5的代价然后就知道了答案是6
// 不能猜6因为最差情况下需要耗费6的代价然后才知道答案是5
// 所以当然选代价低的!请深刻理解:每次永远面临猜错的最差情况!
if (L == R - 1) {
return L;
}
// 如果说明L~R范围不仅仅两个数字
// 比如5 6 7 8 9
// 首先尝试5如果最差情况出现代价为5 + 6~9范围上的尝试
// 最后尝试9如果最差情况出现代价为9 + 5~8范围上的尝试
int ans = Math.min(L + process1(L + 1, R), R + process1(L, R - 1));
// 进而尝试6如果最差情况出现代价为6 + Max { 5~5范围上的尝试 7~9范围上的尝试}
// 这是因为猜了6会告诉你猜高了还是猜低了所以左右两侧的待定范围一定会只走一侧
// 又因为永远会遇到最差情况,所以,一定会走最难受的那一侧,所以是 Max { 5~5范围上的尝试 7~9范围上的尝试}
// 进而尝试7如果最差情况出现代价为7 + Max { 5~6范围上的尝试 8~9范围上的尝试}
// 进而尝试8如果最差情况出现代价为8 + Max { 5~7范围上的尝试 9~9范围上的尝试}
// 所有尝试中,取代价最小值
for (int M = L + 1; M < R; M++) {
ans = Math.min(ans, M + Math.max(process1(L, M - 1), process1(M + 1, R)));
}
return ans;
}
// 上面的暴力递归改动态规划
// 提交到leetcode上可以直接通过
public static int getMoneyAmount2(int n) {
int[][] dp = new int[n + 1][n + 1];
for (int i = 1; i < n; i++) {
dp[i][i + 1] = i;
}
for (int L = n - 2; L >= 1; L--) {
for (int R = L + 2; R <= n; R++) {
dp[L][R] = Math.min(L + dp[L + 1][R], R + dp[L][R - 1]);
for (int M = L + 1; M < R; M++) {
dp[L][R] = Math.min(dp[L][R], M + Math.max(dp[L][M - 1], dp[M + 1][R]));
}
}
}
return dp[1][n];
}
}