题目
思路
玩家都会有两个方法,就是先手拿牌和后手拿牌,先手拿牌的情况分两种拿左边的或者拿右边的,我们取二者中较大的,并且加上剩余牌后手拿的情况。然后分析后手拿的情况,后手拿一定是被动,而且对手已经选择了最好的情况,所以我们返回拿走牌剩余拿到较小的情况。最终的返回条件是只剩最后一张牌了。
代码
递归版
// 根据规则,返回获胜者的分数
public static int win1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int first = f1(arr, 0, arr.length - 1);
int second = g1(arr, 0, arr.length - 1);
return Math.max(first, second);
}
// arr[L..R],先手获得的最好分数返回
public static int f1(int[] arr, int L, int R) {
if (L == R) {
return arr[L];
}
int p1 = arr[L] + g1(arr, L + 1, R);
int p2 = arr[R] + g1(arr, L, R - 1);
return Math.max(p1, p2);
}
// // arr[L..R],后手获得的最好分数返回
public static int g1(int[] arr, int L, int R) {
if (L == R) {
return 0;
}
int p1 = f1(arr, L + 1, R); // 对手拿走了L位置的数
int p2 = f1(arr, L, R - 1); // 对手拿走了R位置的数
return Math.min(p1, p2);//对手留给你的肯定是最差的
}
动态规划版
public static int win3(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int N = arr.length;
int[][] f = new int[N][N];
int[][] g = new int[N][N];
for (int i = 0; i < N; i++) {
f[i][i] = arr[i];//初始化对角线的值
}
for (int i = 1; i < N; i++) {
int L = 0;
int R = i;
while (R < N) {
f[L][R] = Math.max(arr[L] + g[L + 1][R], arr[R] + g[L][R - 1]);
g[L][R] = Math.min(f[L + 1][R], f[L][R - 1]);
L++;
R++;
}
}
return Math.max(f[0][N - 1], g[0][N - 1]);
}