问题描述:
整型数组array,代表数值不同的纸牌排成一条线,玩家a和b依次拿走每张,规定a先拿,b后拿;每次只能拿最左或最右的,a和b都很聪明,请返回胜者的分数。
举例数组:arr = { 8, 6, 7, 9 };
要求返回最大值。
解决方案:此处我们采用三种方案进行,基本思路是从暴力递归到动态规划的演变。
1、暴力递归:核心代码如下
public class NanDaoCardsInLine {
public static void main(String[] args) {
int[] arr = { 8, 6, 7, 9 };
System.out.println(success1(arr));
}
/**
* 暴力递归方案
* @param arr
* @return
*/
private static int success1(int[] arr) {
if(arr == null || arr.length == 0){
return 0;
}
int first = f1(arr,0,arr.length - 1);//a先取数据的
int second = s1(arr,0,arr.length - 1);//b后取数据
return Math.max(first,second);//返回最大的分数
}
private static int s1(int[] arr, int L, int R) {
if(L == R){
return 0;//相等时肯定被a取走
}
int sum1 = f1(arr,L + 1,R);//a取了L位置的数
int sum2 = f1(arr,L,R - 1);//a取了R位置的数
return Math.min(sum1,sum2);//返回最小的分数
}
private static int f1(int[] arr, int L, int R) {
if(L == R){
return arr[L];//相等时肯定被a取走
}
int sum1 = arr[L] + s1(arr,L + 1,R);//a取了L位置的数,然后变成了后取数据的角色
int sum2 = arr[R] + s1(arr,L,R - 1);//a取了R位置的数,然后变成了后取数据的角色
return Math.max(sum1,sum2);//返回最大的分数
}
}
2、加缓存方案:
2.1、分析重复现象的图示:
有重复现象就可以加缓存过滤重复值。
2.2、核心代码如下:
public class NanDaoCardsInLine {
public static void main(String[] args) {
int[] arr = { 8, 6, 7, 9 };
System.out.println(success2(arr));
}
/**
* 杀缓存方案
* @param arr
* @return
*/
private static int success2(int[] arr) {
if(arr == null || arr.length == 0){
return 0;
}
int N = arr.length;
int[][] fCache = new int[N][N];//a的缓存
int[][] sCache = new int[N][N];//b的缓存
//赋默认值
for(int i = 0;i < N;i++){
for(int j = 0;j < N;j++){
fCache[i][j] = -1;
sCache[i][j] = -1;
}
}
int sum1 = f2(arr,0,N - 1,fCache,sCache);
int sum2 = s2(arr,0,N - 1,fCache,sCache);
return Math.max(sum1,sum2);
}
private static int s2(int[] arr, int L, int R, int[][] fCache, int[][] sCache) {
//如果已经赋值即缓存中有数据,就直接返回
if(sCache[L][R] != -1){
return sCache[L][R];
}
int res = 0;
if(L != R){
int sum1= f2(arr,L + 1,R,fCache,sCache);
int sum2= f2(arr,L,R - 1,fCache,sCache);
res = Math.min(sum1,sum2);
}
sCache[L][R] = res;//缓存中赋值
return res;
}
private static int f2(int[] arr, int L, int R, int[][] fCache, int[][] sCache) {
//如果已经赋值即缓存中有数据,就直接返回
if(fCache[L][R] != -1){
return fCache[L][R];
}
int res = 0;
if(L == R){
res = arr[L];
}else {
int sum1 = arr[L] + s2(arr,L + 1,R,fCache,sCache);
int sum2 = arr[R] + s2(arr,L,R - 1,fCache,sCache);
res = Math.max(sum1,sum2);
}
fCache[L][R] = res;//缓存中赋值
return res;
}
}
3、标准动态规划:
3.1、先取即a的二维数组
L > R 的不合逻辑的地方用 * 号标记。
3.1、后取即b的二维数组
L > R 的不合逻辑的地方用 * 号标记。
3.3、核心代码如下:
public class NanDaoCardsInLine {
public static void main(String[] args) {
int[] arr = { 8, 6, 7, 9 };
System.out.println(success3(arr));
}
/**
* 动态规划方案
* @param arr
* @return
*/
private static int success3(int[] arr) {
if(arr == null || arr.length == 0){
return 0;
}
int N = arr.length;
int[][] f3 = new int[N][N];//a的缓存
int[][] s3 = new int[N][N];//b的缓存
//a的对角线赋默认值,b中对角线是0
for(int i = 0;i < N;i++){
f3[i][i] = arr[i];
}
for(int start = 1;start < N;start++){
int L = 0;
int R = start;
while (R < N){//防止数组越界
f3[L][R] = Math.max(arr[L] + s3[L + 1][R],arr[R] + s3[L][R - 1]);//a二维数组中赋值
s3[L][R] = Math.min(f3[L + 1][R],f3[L][R - 1]);//b二维数组中赋值
L++;
R++;
}
}
return Math.max(f3[0][N - 1],s3[0][N - 1]);
}
}
4、三种方案执行结果:
到此,三种方案分享完毕,大家一定要多思考,勤练习,定会进步很快!