有一排正数,玩家A和玩家B都可以看到。
每位玩家在拿走数字的时候,都只能从最左和最右的数中选择一个。玩家A先拿,玩家B再拿,两人交替拿走所有的数字,
两人都力争自己拿到的数的总和比对方多。请返回最后获胜者的分数。
例如:
5,2,3,4
玩家A先拿,当前他只能拿走5或者4。
如果玩家A拿走5,那么剩下2,3,4。轮到玩家B,此时玩家B可以选择2或4中的一个,…
如果玩家A拿走4,那么剩下5,2,3。轮到玩家B,此时玩家B可以选择5或3中的一个,…
Win1思路:
用函数f和s分别表示先发和后发的情况下,在所剩区间选择剩余数字能得到的最大值。
返回先发者和后发者两者中的最大分数
Win2思路:
Win2是在win1的基础上做的优化,把f和s的值分别用一张二维表来描述,f[I,j]表示剩余i到j区间上的牌时,先选能取得的最大收益。
观察win1函数可以发现,其最终要求的是f(arr,0,N-1),这个值对应于f表中的最右上角的位置,而根据f函数又可以发现,该位置依赖s表中对应位置的下一个位置和左边的一个位置。
同理,s表中某位置的值也依赖于f表中相应位置的下一个位置和左边位置的值。又已知f表中对角线的位置为1,s表中对角线位置上的元素为0,就这样根据这两个表的值,往表的上面推,可以推出右上角位置上的元素。
Win3思路:
Win3又是在Win1的基础上进一步优化,p函数可看作我先发,在剩余区间i到j上的最大收益。
当剩余一个数时,则收益即为该值
当剩余两个数时,肯定选大的:
(1)当我拿最左边的数,即arr[i]:则他可能拿剩下的最左边arr[i+1]和最右边的数arr[j];当对方拿arr[i+1]时,我就只能拿[i+2,j]中的数,当对方拿arr[j]时,我只能拿[i+1,j-1]中的数,但是我拿的肯定是这两个数中的最小值
当我拿最右边的数,即arr[j]:则他可能拿剩下的最左边arr[i]和最右边的数arr[j-1];当对方拿arr[i]时,我就只能拿[i+1,j]中的数,当对方拿arr[j-1]时,我只能拿[i,j-2]中的数,但是我拿的肯定还是这两个数中的最小值
到底我拿最左边还是左右边,哪种收益高就拿哪种
Win4:动态规划
public class Problem_03_CardsInLine {
public static int win1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
}
//函数f表示先发的情况下,剩余数下标区间为[i,j],能取得的最大收益
public static int f(int[] arr, int i, int j) {
//剩余一个数,我先发,则必然我取
if (i == j) {
return arr[i];
}
//作为先发,我取第i个数的收益+我作为[i+1,j]的后发者的收益 或 我取第j个数的收益+我作为[i,j-1]上的后发者的收益 中的最大值
return Math.max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));
}
//s表示后发情况下,在剩余数下标区间为[i,j]中,能取得的最大收益
public static int s(int[] arr, int i, int j) {
//作为后发者,剩余一个数的时候肯定不是我取,因此收益为0
if (i == j) {
return 0;
}
//作为后发者,先发者会让我在[i+1,j]或[i,j-1]上取最小的收益
return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));
}
public static int win2(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int[][] f = new int[arr.length][arr.length];
int[][] s = new int[arr.length][arr.length]; //s会自动初始化为0
for (int j = 0; j < arr.length; j++) {
f[j][j] = arr[j];
for (int i = j - 1; i >= 0; i--) {
f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);
}
}
return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);
}
public static int win3(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
int scores = p(arr, 0, arr.length - 1);
return Math.max(sum - scores, scores);
}
public static int p(int[] arr, int i, int j) {
if (i == j) {
return arr[i];
}
if (i + 1 == j) {
return Math.max(arr[i], arr[j]);
}
return Math.max(arr[i] + Math.min(p(arr, i + 2, j), p(arr, i + 1, j - 1)),
arr[j] + Math.min(p(arr, i + 1, j - 1), p(arr, i, j - 2)));
}
public static int win4(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
if (arr.length == 1) {
return arr[0];
}
if (arr.length == 2) {
return Math.max(arr[0], arr[1]);
}
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
int[][] dp = new int[arr.length][arr.length];
for (int i = 0; i < arr.length - 1; i++) {
dp[i][i] = arr[i];
dp[i][i + 1] = Math.max(arr[i], arr[i + 1]);
}
dp[arr.length - 1][arr.length - 1] = arr[arr.length - 1];
for (int k = 2; k < arr.length; k++) {
for (int j = k; j < arr.length; j++) {
int i = j - k;
dp[i][j] = Math.max(arr[i] + Math.min(dp[i + 2][j], dp[i + 1][j - 1]),
arr[j] + Math.min(dp[i + 1][j - 1], dp[i][j - 2]));
}
}
return Math.max(dp[0][arr.length - 1], sum - dp[0][arr.length - 1]);
}
public static int[] generateRondomArray() {
int[] res = new int[(int) (Math.random() * 20) + 1];
for (int i = 0; i < res.length; i++) {
res[i] = (int) (Math.random() * 20) + 1;
}
return res;
}
public static void main(String[] args) {
int testTime = 50000;
boolean err = false;
for (int i = 0; i < testTime; i++) {
int[] arr = generateRondomArray();
int r1 = win1(arr);
int r2 = win2(arr);
int r3 = win3(arr);
int r4 = win4(arr);
if (r1 != r2 || r1 != r3 || r1 != r4) {
err = true;
}
}
if (err) {
System.out.println("2333333333");
} else {
System.out.println("6666666666");
}
}
}