题目描述:
给定一个整形数组arr,代表数值不同的纸牌排成一条线
玩家A和玩家B一次拿走每张纸牌
规定玩家A先拿,玩家B后拿
但是每个玩家每次只能只能拿走最左或者最右的纸牌
双方玩家都会使得对方在改变策略的情况下,对方不会获得更大收益
请问最后获胜者的分数?
思路分析:
同一个轮次,面对同样的L R, 先手必然会选较大的值,后首面对的也是同样的L R,但因为是先手选完之后才选择,所以 只能选其中较小的值。
详解:
定义两个函数: F( arr, L, R),表示在[L,R]上,先手选择可以获得的最大分数。S( arr, L, R),表示在[L,R]上,后手选择可以获得的最大分数
先手F( arr, L, R)函数:
base case: 当只有一张牌(L==R),只能选择该张牌
选L:如果选择的是 L位置上的牌,那么接下来会选 [L+1,R]上 后手选择可以获得的最大分数,即 S(arr,L+1,R),此时最大得分是 arr[L]+S(arr,L+1,R);
选R:如果选择的是 R位置上的牌,那么接下来会选 [L,R-1]上 后手选择可以获得的最大分数,即 S(arr,L,R-1),此时最大得分是 arr[R]+S(arr,L,R-1);
决策:最后选择以上两种情况的较大值,作为自己的分数
后手S(arr,L,R)函数:
base case: 当只有一张牌(L==R),由于是后手选择,当对手把该张牌选走,就没有牌,即 得分为0
如果对手先手挑走了L:此时就相当于我们在[L+1,R]上 先手选择可以获得的最大分数,即F( arr, L+1, R)
如果对手先手挑走了R:此时就相当于我们在[L,R-1]上 先手选择可以获得的最大分数,即F( arr, L, R-1)
决策:由于是 后手选牌,自己 没有决定权, 对手一定会按照对自己最坏的情况进行选择,即 我们只能是上面两种情况的较小值,作为自己的分数
可以大致看一下下面这个简单的实例:
![](https://img-blog.csdnimg.cn/img_convert/ba1e0368510358aa31147e9e48fed81c.png)
在开始看具体代码前,我们 必须要十分明确F(arr,L,R)方法和S(arr,L,R)方法的定义,这一点很重要!!!
F( arr, L, R),表示在[L,R]上,先手选择可以获得的最大分数
S( arr, L, R),表示在[L,R]上,后手选择可以获得的最大分数
在设计F( arr, L, R)时,会有 S( arr, L, R)的涉及,我们可以把他当做一个 黑盒,只需要明白他的作用,后面再写出他的方法体!
具体代码:
//这是主方法,我们先看下面的f方法和s方法,再回来看win方法
public static int win(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)
);
}
//先手选择,在L~R上获得的最大分数
public static int f(int[] arr, intL, intR) {
//如果只剩一张牌,那么只能选这张牌
if (L==R) {
return arr[L];
}
//从选左和选右中的较大值
return Math.max(
arr[L] +s(arr, L+1, R),
arr[R] +s(arr, L, R-1)
);
}
//后手选择,在L~R上获得的最大分数
public static int s(int[] arr, intL, intR) {
//如果只剩一张牌,由于是后手,所以没得选
if (L==R) {
return 0;
}
//当对方选完,变成你先手时,你只能选择对方留给你的较小值
return Math.min(
f(arr, L+1, R),
f(arr, L, R-1)
);
}
第一次写,写的不好或者有其他意见,你的都是对的