本文主要是介绍力扣 | 递归 | 区间上的动态规划 | 486. 预测赢家,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 一、递归
- 二、区间动态规划
LeetCode:486. 预测赢家
一、递归
注意到本题数据范围为 1 < = n < = 20 1<=n<=20 1<=n<=20,因此可以使用递归枚举选择方式,时间复杂度为 2 20 = 1024 ∗ 1024 = 1048576 = 1.05 × 1 0 6 2^{20} = 1024*1024=1048576=1.05 × 10^6 220=1024∗1024=1048576=1.05×106。
对于每一个先手都有两种选择方式,我们对该选择方式进行枚举。
我们定义递归函数 p r e d i c t predict predict表示玩家1在当前选择的情况下是否可以胜利,注意到每个玩家的玩法都会使他的分数最大化
,因此对于玩家2的选择,如果存在一个选择使得玩家1输,那么该情况下玩家1都不能胜利(只要玩家2选择让自己赢的情况,那么玩家1就不能赢了)。但是对于玩家1的选择,存在一个选择能赢就算可以赢。
class Solution {
public:bool predictTheWinner(vector<int>& nums) {return predict(nums, 0, (int) nums.size() - 1, 0, 0, 0);}
private:bool predict(vector<int> & nums, int left, int right, int player, int score_1, int score_2){if(left > right){if(score_1 >= score_2) return true;else return false;}if(player == 0){//玩家一,存在一个赢就算赢了if(predict(nums, left + 1, right, player ^ 1, score_1 + nums[left], score_2)) return true;if(predict(nums, left, right - 1, player ^ 1, score_1 + nums[right], score_2)) return true;}else{//玩家二,存在一个玩家二赢,则玩家一必输。if(predict(nums, left + 1, right, player ^ 1, score_1, score_2 + nums[left]) &&predict(nums, left, right - 1, player ^ 1, score_1, score_2 + nums[right])) return true;}return false;}
};
二、区间动态规划
注意到这里是从大的数组中选择,让数组依次减小,我们也可以从数组小的开始转移到大数组,因为当小数组确定时,大数组也能确定。
我们可以定义 d p 1 [ i ] [ j ] [ p l a y e r ] dp1[i][j][player] dp1[i][j][player]表示在选择数组 [ i , j ] [i,j] [i,j]时, p l a y e r player player先手时,玩家1的分数;类似的有 d p 2 [ i ] [ j ] [ p l a y e r ] dp2[i][j][player] dp2[i][j][player]。
则有状态转移:
//玩家1先手dp1[i][j][0] = max(dp1[i + 1][j][1] + nums[i], dp1[i][j - 1][1] + nums[j]);if(dp1[i + 1][j][1] + nums[i] >= dp1[i][j - 1][1] + nums[j]){dp2[i][j][0] = dp2[i + 1][j][1];if(dp1[i + 1][j][1] + nums[i] == dp1[i][j - 1][1] + nums[j])dp2[i][j][0] = min(dp2[i + 1][j][1], dp2[i][j - 1][1]);}else{dp2[i][j][0] = dp2[i][j - 1][1];}
//玩家2先手dp2[i][j][1] = max(dp2[i + 1][j][0] + nums[i], dp2[i][j - 1][0] + nums[j]);if(dp2[i + 1][j][0] + nums[i] >= dp2[i][j - 1][0] + nums[j]){dp1[i][j][1] = dp1[i + 1][j][0];if(dp2[i + 1][j][0] + nums[i] == dp2[i][j - 1][0] + nums[j])dp1[i][j][1] = min(dp1[i + 1][j][0], dp1[i][j - 1][0]);}else{dp1[i][j][1] = dp1[i][j - 1][0];}
时间复杂度: O ( N 2 ) O(N^2) O(N2)
class Solution {
public:bool predictTheWinner(vector<int>& nums) {vector<vector<array<int, 2>>> dp1(nums.size(), vector<array<int, 2>>(nums.size(), array<int, 2>{}));vector<vector<array<int, 2>>> dp2(nums.size(), vector<array<int, 2>>(nums.size(), array<int, 2>{}));for(int i = 0; i < nums.size(); ++ i){dp1[i][i][0] = nums[i];dp2[i][i][1] = nums[i];}for(int k = 1; k < nums.size(); ++ k){for(int i = 0; k + i < nums.size(); ++ i){int j = i + k;//玩家1先手dp1[i][j][0] = max(dp1[i + 1][j][1] + nums[i], dp1[i][j - 1][1] + nums[j]);if(dp1[i + 1][j][1] + nums[i] >= dp1[i][j - 1][1] + nums[j]){dp2[i][j][0] = dp2[i + 1][j][1];if(dp1[i + 1][j][1] + nums[i] == dp1[i][j - 1][1] + nums[j])dp2[i][j][0] = min(dp2[i + 1][j][1], dp2[i][j - 1][1]);}else{dp2[i][j][0] = dp2[i][j - 1][1];}//玩家2先手dp2[i][j][1] = max(dp2[i + 1][j][0] + nums[i], dp2[i][j - 1][0] + nums[j]);if(dp2[i + 1][j][0] + nums[i] >= dp2[i][j - 1][0] + nums[j]){dp1[i][j][1] = dp1[i + 1][j][0];if(dp2[i + 1][j][0] + nums[i] == dp2[i][j - 1][0] + nums[j])dp1[i][j][1] = min(dp1[i + 1][j][0], dp1[i][j - 1][0]);}else{dp1[i][j][1] = dp1[i][j - 1][0];}}}return dp1[0][(int) nums.size() - 1][0] >= dp2[0][(int) nums.size() - 1][0];}
};
简化代码:(这个代码过了)
简化逻辑:玩家1先手,那么玩家1效益最大,玩家2应该效益要最低。
但这个逻辑感觉存在一定问题,因为玩家1先手,玩家1想要自己的效益最大,这个时候玩家2的效益是跟玩家1的选择有关的,因为棋局完全根据玩家1来决定。所以一开始我并没有直接使用min
求解后手。
class Solution {
public:bool predictTheWinner(vector<int>& nums) {vector<vector<array<int, 2>>> dp1(nums.size(), vector<array<int, 2>>(nums.size(), array<int, 2>{}));vector<vector<array<int, 2>>> dp2(nums.size(), vector<array<int, 2>>(nums.size(), array<int, 2>{}));for(int i = 0; i < nums.size(); ++ i){dp1[i][i][0] = nums[i];dp2[i][i][1] = nums[i];}for(int k = 1; k < nums.size(); ++ k){for(int i = 0; k + i < nums.size(); ++ i){int j = i + k;//玩家1先手dp1[i][j][0] = max(dp1[i + 1][j][1] + nums[i], dp1[i][j - 1][1] + nums[j]);dp2[i][j][0] = min(dp2[i + 1][j][1], dp2[i][j - 1][1]);//玩家2先手dp2[i][j][1] = max(dp2[i + 1][j][0] + nums[i], dp2[i][j - 1][0] + nums[j]);dp1[i][j][1] = min(dp1[i + 1][j][0], dp1[i][j - 1][0]);}}return dp1[0][(int) nums.size() - 1][0] >= dp2[0][(int) nums.size() - 1][0];}
};
这篇关于力扣 | 递归 | 区间上的动态规划 | 486. 预测赢家的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!