纸牌游戏
-
给定一个整型数组arr,代表数值不同的纸牌排成一条线玩家A和玩家B依次拿走每张纸牌
- 规定玩家A先拿,玩家B后拿
- 但是每个玩家每次只能拿走最左或最右的纸牌玩家A和玩家B都绝顶聪明
- 请返回最后获胜者的分数。
-
暴力递归
- 加上对于一个数组先手获得最大的分数方法为
int FirstTake(vector<int>& nums,int left, int right)
- 可以转化为“先拿左边+对剩下的牌后手获得的最大分数”和“先拿右边+对剩下的牌后手获得的最大分数”,返回两者较大的
- 加上对于一个数组先手获得最大的分数方法为
int WhoWin(vector<int>& nums) {
int point_A = FirstTake(nums,0,nums.size()-1);
int point_B = SecondTake(nums,0,nums.size()-1);
return max(point_A, point_B);
}
int FirstTake(vector<int>& nums,int left, int right){
//先思考边界条件 只剩一张牌
if(left == right) return nums[left];
//拿左边
int point_left = nums[left]+SecondTake(vector<int>& nums, int left+1,right);
//拿右边
int point_right = nums[right]+SecondTake(vector<int>& nums, int left,right-1);
return max(point_left, point_right);
}
int SecondTake(vector<int>& nums,int left, int right) {
//先思考边界情况,只剩一张牌
if(left == right) return 0;
//对方拿左边
int point_left = FirstTake(vector<int>& nums, int left+1,right);
//对方拿右边
int point_right = FirstTake(vector<int>& nums, int left,right-1);
//因为对方会保证最优,我们只可能拿到比较小的那一个
return min(point_left, point_right);
}
- 具体举例,发现有重复子集,运用记忆化搜索
int WhoWin(vector<int>& nums) {
vector<vector<int>> f_dp(nums.size()-1,vector<int>(nums.size()-1,-1));
vector<vector<int>> s_dp(nums.size()-1,vector<int>(nums.size()-1,-1));
int point_A = FirstTake(nums,0,nums.size()-1,f_dp,s_dp);
int point_B = SecondTake(nums,0,nums.size()-1,f_dp,s_dp);
return max(point_A, point_B);
}
int FirstTake(vector<int>& nums,int left, int right, vector<vector<int>>& f_dp,vector<vector<int>>& s_dp){
if(f_dp[left][right] != -1) return f_dp[left][right];
//先思考边界条件 只剩一张牌
if(left == right) return f_dp[left][right] = nums[left];
//拿左边
int point_left = nums[left]+SecondTake(nums,left+1,right,f_dp,s_dp);
//拿右边
int point_right = nums[right]+SecondTake(nums,left,right-1,f_dp,s_dp);
return f_dp[left][right] = max(point_left, point_right);
}
int SecondTake(vector<int>& nums,int left, int right, vector<vector<int>>& f_dp,vector<vector<int>>& s_dp) {
if(s_dp[left][right] != -1) return s_dp[left][right];
//先思考边界情况,只剩一张牌
if(left == right) return s_dp[left][right] = 0;
//对方拿左边
int point_left = FirstTake(nums,left+1,right,f_dp,s_dp);
//对方拿右边
int point_right = FirstTake(nums,left,right-1,f_dp,s_dp);
//因为对方会保证最优,我们只可能拿到比较小的那一个
return s_dp[left][right] = min(point_left, point_right);
}