首页 > 编程语言 >[题解]轮流拿牌问题_一道博弈论笔试题(C++)

[题解]轮流拿牌问题_一道博弈论笔试题(C++)

时间:2022-08-23 12:23:36浏览次数:151  
标签:vector return nums int 题解 复杂度 C++ 先手 拿牌

题目

A和B轮流从一个数组左右两端取数,A先B后,每次取一个数,最终取数总和大者获胜,两人每次都会选择最有利的策略,求获胜者取数的和。

思路

笔试时遇到的一道算法题,也是博弈论中非常经典的入门题目了。从先后手的角度考虑,先手在行动一次后获得左右两端数中的一个,然后转换为后手;而后手在先手取完一个数之后,转换为先手。两人每次都会选择最有力的策略,因此先手变为后手后采取的策略与后手相同,反之亦然。先手一定会采取使得自己分数最大的行动,同时迫使后手拿到其进入先手后能拿到的最小分数。这是本题中的先手优势。
采用递归的方法,先手函数first()返回先手能拿到的最大分数,后手函数second()返回后手能拿到的最大分数,两者相互调用。
时间复杂度O(\(2^n\)),空间复杂度O(n)。

代码

class Solution {
public:
    int win(vector<int>& nums) {
        int n = nums.size();
        if (n == 0) return 0;
        // 返回先手和后手分数更大的值
        return max(first(nums, 0, n - 1), second(nums, 0, n - 1));
    }

    int first(vector<int>& nums, int i, int j) {
        if (i == j) {
            // 只剩一个数时,先手会拿走这个数
            return nums[i];
        }
        // 拿走左右两端中一个数后,采取后手策略
        return max(nums[i] + second(nums, i + 1, j), nums[j] + second(nums, i, j - 1));
    }

    int second(vector<int>& nums, int i, int j) {
        if (i == j) {
            // 只剩一个数时,后手拿不到数,返回0
            return 0;
        }
        // 先手拿走两端一个数后,后手采取先手策略,先手一定会保证后手只能拿到两种情况下更小的那种
        return min(first(nums, i + 1, j), first(nums, i, j - 1));
    }
};

改进

可以用动态规划替代递归,降低时间复杂度。定义两个二维数组f[n][n]和s[n][n],f[i][j]表示先手取i到j范围的最大值,s[i][j]表示后手取i到j范围的最大值。从内往外迭代。
时间复杂度O(\(n^2\)),空间复杂度O(\(n^2\))。

代码

class Solution {
public:
    int win(vector<int>& nums) {
        if (nums.empty()) return 0;
        int n = nums.size();
        vector<vector<int>> f(n, vector<int>(n, 0));
        vector<vector<int>> s(n, vector<int>(n, 0));
        
        for (int i = 0; i < n; ++i) {
            f[i][i] = nums[i];
        }

        for (int i = 1; i < n; ++i) {
            for (int L = 0, R = i; R < n; ++L, ++R) {
                f[L][R] = max(nums[L] + s[L + 1][R], nums[R] + s[L][R - 1]);
                s[L][R] = min(f[L + 1][R], f[L][R - 1]);
            }
        }

        return max(f[0][n - 1], s[0][n - 1]);
    }
};

标签:vector,return,nums,int,题解,复杂度,C++,先手,拿牌
From: https://www.cnblogs.com/fusheng-chana/p/16615545.html

相关文章

  • c++和python混合编程,调用了CTP的附加库 (windows)(应该是全网第一篇)
    这是一个连接券商的代码,simnow提供的包,windows版,linux的话,下一篇文章介绍听起来就很复杂,所以需要大家有点功底,不懂的东西,多多百度,因为很多细节,我不可能还教怎么使用visual......
  • ARC099F题解
    被杀了,记录一下好了。对于他那个数组是否相等,直接判断复杂度很高,考虑通过哈希映射之后判断是否相等。对数组的Hash可以类似字符串Hash那样去做。于是判断一个区间是......
  • laravel+mews/captcha 打开页面后的首次验证码总是验证失败的问题解决
    出现问题的原因验证码获取后,还有其他的接口请求,导致验证码的缓存被覆盖(参考文章:LaravelSession遇到的坑)解决办法修改vendor/mews/captcha/src/Captcha.php源码,将......
  • AtCoder Grand Contest 058 部分题目不简要题解
    从这里开始比赛目录ProblemA MakeitZigzag考虑使$1,3,5,7,\cdots,2n-3$这些位置后三个中的最大值在中间,最后再处理一下最后两个位置就行了。Cod......
  • ECfinal2021部分题解
    把赛中没有过的题争取补一下题目链接:https://codeforces.com/gym/103861C:其实,最后每一种字符只有两种状态:1.出现了x,此时就已经知道该字符有多少个了2.没有出现x,那么相......
  • C++ 黑客攻击系统
    #include<iostream>#include<Windows.h>#include<string>#include<conio.h>//getch使用#include"hacker.h"usingnamespacestd;#defineWIDTH40#define......
  • C++ 黑客攻击系统实现
    #include<iostream>#include<Windows.h>#include<string>#include<conio.h>//getch使用#include"hacker.h"usingnamespacestd;#defineWIDTH40#define......
  • C++ 函数的定义
     函数的定义: 1.确定函数的功能; 2.确定函数的参数; 3.确定函数的返回值; 4.确定函数名; 5.函数的实现。 #include<iostream>usingnamespacestd;intsum(intn)......
  • C++Beginner(3)-Compile
    compilingsourcecodefile(.cpp,.cxx,.cc,.C,.c++)->objectfiles(.o,.obj)->linkobjectfilestogetherintoanexecutable(app.exe,app),staticlibrary(.lib......
  • c++ 智能指针
    智能指针,是模板类,意在避免在使用动态内存时,出现异常等意外,或忘记使用delete,而造成内存泄漏。这个智能指针,在指针变量结束声明周期后,调用对象的析构函数,并自动去释放这个指......