玩家博弈问题题目
给定一个整型数组,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌。规定玩家A先拿,玩家B后拿。但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明。请返回最后获胜者的分数。
分析
由于玩家A先拿,所以会先从左侧或者右侧选择一张,同时令后手的B获得的数值最小。如果令f(int from, int to)为从from到to范围上先手拿时获得的总价值最高的选择,g(int from, int to)为from到to范围上后手拿时获得的总价值最高的选择,则:
暴力递归代码
// 根据规则,返回获胜者的分数
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);
}
static int f(int[] arr, int L, int R) {
if (L == R) {
return arr[L];
}
int p1 = arr[L] + g(arr, L + 1, R);
int p2 = arr[R] + g(arr, L, R - 1);
return Math.max(p1, p2);
}
static int g(int[] arr, int L, int R) {
if (L == R) return 0;
int p1 = f(arr, L + 1, R);
int p2 = f(arr, L, R - 1);
return Math.min(p1, p2);
}
改为动态规划
static int windp(int[] arr) {
if (arr == null || arr.length < 1) return 0;
int len = arr.length;
int[][] gmap = new int[len][len];
int[][] fmap = new int[len][len];
for (int i = 0; i < len; i++) {
gmap[i][i] = 0;
fmap[i][i] = arr[i];
}
//除此之外,L一定不大于R,也就是取矩阵的上半部分
for (int j = 1; j < len; j++) {
int L = 0;
int R = j;
while (R < len) {
fmap[L][R] = Math.max(arr[L] + gmap[L + 1][R], arr[R] + gmap[L][R - 1]);
gmap[L][R] = Math.min(fmap[L + 1][R], fmap[L][R - 1]);
L++;
R++;
}
}
return Math.max(fmap[0][len - 1],gmap[0][len - 1]);
}
总结
这个题说到底还是由暴力递归改来的,而暴力递归就是朴素的尝试,在这个题中,题目中要求要每次取牌的时候,要么从最左取要么从最右取,这就是两种可能性,把这两种可能性列出来,就可以解出来了。