LeetCode 486: Predict the Winner 解题与思考

#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+j1)

若$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+j1)

这里为了更新的顺序,以j为外层循环,i为内层循环,保证每个需要用到的值在使用之前均能够被计算出来。

最终计算 f i r s t _ p i c k ( 0 , n − 1 ) first\_pick(0, n - 1) first_pick(0,n1)是否大于 s e c o n d _ p i c k ( 0 , n − 1 ) second\_pick(0, n - 1) second_pick(0,n1)即可

##代码

#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时,本来是平局,但是标准测例还是算了先手获胜,所以最后的大于号换成了大于等于号,问题解决

你可能感兴趣的:(LeetCode)