目录
题目二
给定一个整型数组arr,代表数值不同的纸牌排成一条线玩家A和玩家B依次拿走每张纸牌
规定玩家A先拿,玩家B后拿
但是每个玩家每次只能拿走最左或最右的纸牌玩家A和玩家B都绝顶聪明
请返回最后获胜者的分数。
暴力递归:
思路:由于最后要比较玩家A和玩家B的分数大小,所以要返回两个玩家的分数,因此假定先手拿牌返回分数的方法为 f();后手拿牌返回分数的方法为 g()。
假定数组最左边索引为L,最右边索引为R。
对于 f():
-
当 L == R 时,仅剩最后一张牌,直接返回L;
-
当先手拿L时,等同于下一次后手在(L+1,R)中拿一张;当先手拿R时,等同于下一次后手在(L,R-1)中拿一张;累加;返回两种情况中的最大值。
对于 g():
-
当 L == R时,仅剩最后一张牌,由于是后手拿,最后一张牌会被前面的人拿走,所以返回0;
-
当L被拿走时,等同于这次先手在(L+1,R)中拿一张;当R被拿走时,等同于这次先手在(L,R-1)中拿一张;返回两种情况中的最小值。 注:因为两人绝顶聪明,因此先手的人总会把两种方法中所剩牌累加最小的方法留给后手的人,因此返回最小值。
代码如下:
//纸牌游戏(暴力递归到动态规划)
//主方法
public static int MainWay (int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int L = 0;
int R = arr.length-1;
int first = f(arr,L,R);
int second = g(arr,L,R);
return Math.max(first,second);
}
//先手拿牌 f()方法
public 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);
}
//后手拿牌 g()方法
public static int g(int[] arr, int L, int R) {
if (L == R) {
return 0;
}
int p1 = f(arr,L+1,R); //L被先手拿走
int p2 = f(arr,L,R-1); //R被先手拿走
return Math.min(p1,p2);
}
输出如下:
public static void main(String[] args) {
int[] arr = {5,7,4,5,8,1,6,0,3,4,6,1,7};
System.out.println(MainWay(arr));
}
//输出结果:
//32
动态规划:
分析可知:暴力递归的过程中存在重复的运算,因此我们可以对其进行优化。
-
优化的方法是根据L、R两个变量建立"二维缓存表";
-
对"二维缓存表赋值";这里用两张表来对应 f()与 g();
-
在从暴力递归到动态规划的过程中,暴力递归中的算法往往是建立"缓存表"的关键。我们通过暴力递归中的算法对"缓存表"进行填表赋值。
简而言之:暴力递归的过程是动态规划时给表赋值的方法。
//动态规划:
//根据暴力递归建立个“L-R 二维缓冲表”,通过二维表返回较大分数的一方
public static int way2 (int[] arr, int L, int R) {
if (arr == null || arr.length == 0) {
return 0;
}
int N = arr.length;
//创建二维表
int[][] fmap = new int[N][N]; //f()的L-R二维表
int[][] gmap = new int[N][N]; //g()的L-R二维表
//填表
for (int i = 0; i < N; i++) {
fmap[i][i] = arr[i]; //先手方L==R时的表赋值
}
//L为行;R为列
for (int mapR = 1; mapR < N; mapR++) {
int L1 = 0;
int R1 = mapR;
while (R1 < N) {
fmap[L1][R1] = Math.max(arr[L1] + gmap[L1+1][R1], arr[R1]
+ gmap[L1][R1-1]);
gmap[L1][R1] = Math.min(fmap[L1+1][R1], fmap[L1][R1-1]);
L1++;
R1++;
}
}
return Math.max(fmap[L][R],gmap[L][R]);//返回分数较大的一方
}
输出结果如下:
public static void main(String[] args) {
int[] ints = {4,2,66,33,44};
int[] arr = {5,7,4,5,8,1,6,0,3,4,6,1,7};
System.out.println(MainWay(arr));
System.out.println(way2(arr,0,arr.length-1));
System.out.println(MainWay(ints));
System.out.println(way2(ints,0, ints.length-1));
}
//输出:
32
32
79
79
上一期文章链接:算法练习:从暴力递归到动态规划1