#LeetCode 486: Predict the Winner 解题与思考
[原题链接]
##题目描述
一组数字,两人轮流去取,每次只能取头部或者尾部,直到把所有数字取完,拿到最大的数字和的获胜,返回先手的人是否可以获胜。
##思路
这个问题和人工智能中的一个最大最小搜索算法类似,有兴趣的可以查找。
解决问题的突破口在于,确立先后手概念。
也就是说,在第一个人(不妨命名为A)先选择一个数字后,他就相当于在剩余的数列中,进行后手选择的;而另外一个人B,则变成了在剩余数列中先手选择的。
而双方选择都建立在一个前提,就是尽可能使自己最终获得的分数最大。
那么就可以得到先后手得分的计算方式。
先手能得到的最大分数,是选择一个数字后加上剩余数列中后手所能选得分数的最大值。
后手所能得到的最大分数,是根据先手选择完成之后,在剩余数列中先手选择所能得到分数的最大值。
于是乎我们用两个数组进行DP求解。
##算法
设在第i个数以及第i+j个数之间先手选择的最大得分为 f i r s t _ p i c k ( i , i + j ) first\_pick(i, i+j) first_pick(i,i+j),后手选择的最大得分为 s e c o n d _ p i c k ( i , i + j ) second\_pick(i,i+ j) second_pick(i,i+j),第i个数为 n u m ( i ) num(i) num(i),选择第一个数的最大得分为 s e l e c t _ f i r s t _ o n e select\_first\_one select_first_one,选择最后一个数的最大得分为 s e l e c t _ l a s t _ o n e select\_last\_one select_last_one(注意这里的第一个和最后一个是相对于i, i + j而言的),那么:
s e l e c t _ f i r s t _ o n e = n u m ( i ) + s e c o n d _ p i c k ( i + 1 , i + j ) select\_first\_one = num(i) + second\_pick(i + 1,i + j) select_first_one=num(i)+second_pick(i+1,i+j)
s e l e c t _ l a s t _ o n e = n u m ( i + j ) + s e c o n d _ p i c k ( i , i + j − 1 ) select\_last\_one = num(i + j) + second\_pick(i, i + j - 1) select_last_one=num(i+j)+second_pick(i,i+j−1)
若$select_first_one > select_last_one $,那么意味着先手选第一个数能得到最大分数,后手在第i+1到i+j之间先手取最大分数
f i r s t _ p i c k ( i , i + j ) = s e l e c t _ f i r s t _ o n e first\_pick(i, i+j) = select\_first\_one first_pick(i,i+j)=select_first_one
s e c o n d _ p i c k ( i , i + j ) = f i r s t _ p i c k ( i + 1 , i + j ) second\_pick(i,i+ j) = first\_pick(i + 1, i+j) second_pick(i,i+j)=first_pick(i+1,i+j)
若$select_first_one < select_last_one $,那么意味着先手选最后一个数能得到最大分数,后手在第i到i+j - 1之间先手取最大分数
f i r s t _ p i c k ( i , i + j ) = s e l e c t _ l a s t _ o n e first\_pick(i, i+j) = select\_last\_one first_pick(i,i+j)=select_last_one
s e c o n d _ p i c k ( i , i + j ) = f i r s t _ p i c k ( i , i + j − 1 ) second\_pick(i,i+ j) = first\_pick(i, i+j-1) second_pick(i,i+j)=first_pick(i,i+j−1)
这里为了更新的顺序,以j为外层循环,i为内层循环,保证每个需要用到的值在使用之前均能够被计算出来。
最终计算 f i r s t _ p i c k ( 0 , n − 1 ) first\_pick(0, n - 1) first_pick(0,n−1)是否大于 s e c o n d _ p i c k ( 0 , n − 1 ) second\_pick(0, n - 1) second_pick(0,n−1)即可
##代码
#include
#include
#include
#include
using namespace std;
class Solution {
public:
bool PredictTheWinner(vector& nums) {
int length = nums.size();
int *first_pick = (int*)malloc(length * length * sizeof(int));
memset(first_pick, 0, length * length * sizeof(int));
int *second_pick = (int*)malloc(length * length * sizeof(int));
memset(second_pick, 0, length * length * sizeof(int));
for ( int i = 0; i < length; i++ ) {
first_pick[i * length + i] = nums[i];
second_pick[i * length + i] = 0;
}
for ( int j = 1; j < length; j++ ) {
for ( int i = 0; i < length - j; i++ ) {
int select_first_one = nums[i] + second_pick[(i + 1) * length + i + j];
int select_last_one = nums[i + j] + second_pick[i * length + i + j - 1];
if ( select_first_one > select_last_one ) {
first_pick[i * length + (i + j)] = select_first_one;
second_pick[i * length + (i + j)] = first_pick[(i + 1)*length + (i + j)];
}
else {
first_pick[i * length + (i + j)] = select_last_one;
second_pick[i * length + (i + j)] = first_pick[(i)*length + (i + j) - 1];
}
}
}
bool result = first_pick[length - 1] >= second_pick[length - 1];
free(first_pick);
free(second_pick);
return result;
}
};
##思考
最后还是没能一遍AC,因为当数据为一个0时,本来是平局,但是标准测例还是算了先手获胜,所以最后的大于号换成了大于等于号,问题解决