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.

106 lines
3.6 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden 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_2022_12_2_week;
import java.util.Arrays;
// 石子游戏中,爱丽丝和鲍勃轮流进行自己的回合,爱丽丝先开始 。
// 有 n 块石子排成一排。
// 每个玩家的回合中,可以从行中 移除 最左边的石头或最右边的石头,
// 并获得与该行中剩余石头值之 和 相等的得分。当没有石头可移除时,得分较高者获胜。
// 鲍勃发现他总是输掉游戏(可怜的鲍勃,他总是输),
// 所以他决定尽力 减小得分的差值 。爱丽丝的目标是最大限度地 扩大得分的差值 。
// 给你一个整数数组 stones ,其中 stones[i] 表示 从左边开始 的第 i 个石头的值,
// 如果爱丽丝和鲍勃都 发挥出最佳水平 ,请返回他们 得分的差值 。
// 测试链接 : https://leetcode.cn/problems/stone-game-vii/
public class Code04_StoneGameVII {
// 会超时但是思路是对的,如果想通过就把这个暴力递归改成下面的动态规划
// 改法课上都讲了
public static int stoneGameVII1(int[] stones) {
int sum = 0;
for (int num : stones) {
sum += num;
}
int alice = f(stones, sum, 0, stones.length - 1);
int bob = s(stones, sum, 0, stones.length - 1);
return Math.abs(alice - bob);
}
// 先手
public static int f(int[] stones, int sum, int L, int R) {
if (L == R) { // 只能一块儿了!
return 0;
}
// p1
// L
int p1 = sum - stones[L] + s(stones, sum - stones[L], L + 1, R);
int against1 = f(stones, sum - stones[L], L + 1, R);
// p2
// R
int p2 = sum - stones[R] + s(stones, sum - stones[R], L, R - 1);
int against2 = f(stones, sum - stones[R], L, R - 1);
return (p1 - against1) > (p2 - against2) ? p1 : p2;
}
// 后手!
public static int s(int[] stones, int sum, int L, int R) {
if (L == R) {
return 0;
}
// 当前的是后手
// 对手,先手!
int against1 = sum - stones[L] + s(stones, sum - stones[L], L + 1, R);
// 当前用户的得分!后手!是对手决定的!
int get1 = f(stones, sum - stones[L], L + 1, R);
int against2 = sum - stones[R] + s(stones, sum - stones[R], L, R - 1);
int get2 = f(stones, sum - stones[R], L, R - 1);
return (against1 - get1) > (against2 - get2) ? get1 : get2;
}
// 动态规划版
public static int stoneGameVII2(int[] stones) {
int N = stones.length;
int[] presum = new int[N + 1];
for (int i = 0; i < N; i++) {
presum[i + 1] = presum[i] + stones[i];
}
int[][] dpf = new int[N][N];
int[][] dps = new int[N][N];
for (int L = N - 2; L >= 0; L--) {
for (int R = L + 1; R < N; R++) {
int sumLR = presum[R + 1] - presum[L];
int a = sumLR - stones[L] + dps[L + 1][R];
int b = dpf[L + 1][R];
int c = sumLR - stones[R] + dps[L][R - 1];
int d = dpf[L][R - 1];
dpf[L][R] = (a - b > c - d) ? a : c;
dps[L][R] = (a - b > c - d) ? b : d;
}
}
return Math.abs(dpf[0][N - 1] - dps[0][N - 1]);
}
// 另一种尝试 + static动态规划表 + 空间压缩 + 尽量优化
// dp[len][i] : 从i出发当长度为len的情况下Alice能比Bob多多少分
// 要注意结算时机!这是这种尝试的核心!
public static int[] dp = new int[1000];
// 时间复杂度和刚才讲的一样!
public int stoneGameVII3(int[] s) {
int n = s.length;
Arrays.fill(dp, 0, n, 0);
if (n % 2 == 0) {
for (int i = 0; i < n; i++) {
dp[i] = s[i];
}
}
boolean alicePick = n % 2 == 0;
for (int len = 2; len <= n; len++, alicePick = !alicePick) {
for (int i = 0, j = len - 1; j < n; i++, j++) {
dp[i] = alicePick ? Math.max(dp[i], dp[i + 1]) : Math.min(dp[i] + s[j], s[i] + dp[i + 1]);
}
}
return dp[0];
}
}