486. 预测赢家(博弈dp/区间dp)

链接:486. 预测赢家 - 力扣(LeetCode)

题解:

区间dp,dpij表示从i,j范围内,先手获得的最大分数

第一层,先手可以从左边拿1个 + numsleft

第二层,后手可以拿从左边拿1个或者从右边拿1个,

第三层,先手从左边left+2,right left+1,right-1区间进行拿取,因为都是最聪明的所以这两种情况取的,最小值

同理第一层,先手可以从右边边拿1个 + numsright

第二层,后手可以拿从左边拿1个或者从右边拿1个,

第三层,先手从左边left+1,right-1 left,right-2区间进行拿取,因为都是最聪明的所以这两种情况取的,最小值

这两种方式的最大值当作先手的结果

硬币排成线 III

区间动态规划问题, 具体定义状态的方式有很多种, 但是大同小异, 时空复杂度都相似. 这里只介绍 version 1 的具体实现.

设定 dpij 表示当前剩余硬币的区间为 i, j 时, 此时该拿硬币的人能获取的最大值.

注意, dpij 并没有包含角色信息, dp0values.length - 1 表示的是先手的人能获得的最大值, 而 dp1values.length -1 表示的则是后手的人能获得的最大值. 需要这样做是因为: 两个人都会采用最优策略.

当前的人的决策就是拿左边还是拿右边, 而下一个人仍然会最优决策, 所以应该是最小值中取最大值:

cpp 复制代码
class Solution {
public:
    bool predictTheWinner(vector<int>& nums) {
        int len = nums.size();
        if (len <= 0) {
            return true;
        }
        int sum = 0;
        for (auto num : nums) {
            sum += num;
        }
        vector<vector<int>> memo(len, vector<int>(len, -1));
        return sum <= 2 * dfs(0, len - 1, nums, memo);
    }
    int dfs(int left, int right, const vector<int>& nums,
            vector<vector<int>>& memo) {
        if (left == right) {
            memo[left][right] = nums[left];
            return nums[left];
        }
        if (left > right) {
            return 0;
        }
        if (memo[left][right] != -1) {
            return memo[left][right];
        }
        int pick_left = min(dfs(left + 1, right - 1, nums, memo),
                            dfs(left + 2, right, nums, memo)) +
                        nums[left];
        int pick_right = min(dfs(left + 1, right - 1, nums, memo),
                             dfs(left, right - 2, nums, memo)) +
                         nums[right];
        memo[left][right] = max(pick_left, pick_right);
        return memo[left][right];
    }
};
cpp 复制代码
// 本题解信息来源于【九章算法】。请勿进行商业转载,非商业转载请注明出处。
// version 1
public class Solution {
    /**
     * @param values: an array of integers
     * @return: a boolean which equals to true if the first player will win
     */
    public boolean firstWillWin(int[] values) {
        // write your code here
        int n = values.length;
        int[][] dp = new int[n + 1][n + 1];
        boolean[][] flag = new boolean[n + 1][n + 1];

        int sum = 0;
        for (int now : values)
            sum += now;

        return sum < 2 * MemorySearch(0, values.length - 1, dp, flag, values);
    }

    int MemorySearch(int left, int right, int[][] dp, boolean[][] flag, int[] values) {

        if (flag[left][right])
            return dp[left][right];
        flag[left][right] = true;
        if (left > right) {
            dp[left][right] = 0;
        } else if (left == right) {
            dp[left][right] = values[left];
        } else if (left + 1 == right) {
            dp[left][right] = Math.max(values[left], values[right]);
        } else {
            int pick_left = Math.min(MemorySearch(left + 2, right, dp, flag, values),
                    MemorySearch(left + 1, right - 1, dp, flag, values)) + values[left];
            int pick_right = Math.min(MemorySearch(left, right - 2, dp, flag, values),
                    MemorySearch(left + 1, right - 1, dp, flag, values)) + values[right];
            dp[left][right] = Math.max(pick_left, pick_right);
        }
        return dp[left][right];
    }
}

// version 2
public class Solution {
    /**
     * @param values: an array of integers
     * @return: a boolean which equals to true if the first player will win
     */
    public boolean firstWillWin(int[] values) {
        int n = values.length;
        int[][] dp = new int[n + 1][n + 1];
        boolean[][] flag = new boolean[n + 1][n + 1];
        int[][] sum = new int[n + 1][n + 1];
        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                sum[i][j] = i == j ? values[j] : sum[i][j - 1] + values[j];
            }
        }
        int allsum = 0;
        for (int now : values)
            allsum += now;

        return allsum < 2 * MemorySearch(0, values.length - 1, dp, flag, values, sum);
    }

    int MemorySearch(int left, int right, int[][] dp, boolean[][] flag, int[] values, int[][] sum) {
        if (flag[left][right])
            return dp[left][right];

        flag[left][right] = true;
        if (left > right) {
            dp[left][right] = 0;
        } else if (left == right) {
            dp[left][right] = values[left];
        } else if (left + 1 == right) {
            dp[left][right] = Math.max(values[left], values[right]);
        } else {
            int cur = Math.min(MemorySearch(left + 1, right, dp, flag, values, sum),
                    MemorySearch(left, right - 1, dp, flag, values, sum));
            dp[left][right] = sum[left][right] - cur;
        }
        return dp[left][right];
    }
}

// 九章动态规划专题班和算法强化班版本
public class Solution {
    /**
     * @param values: an array of integers
     * @return: a boolean which equals to true if the first player will win
     */
    public boolean firstWillWin(int[] A) {
        // write your code here

        int n = A.length;
        if (n == 0) {
            return true;
        }

        int[][] f = new int[n][n];
        int i, j, len;
        // len 1
        for (i = 0; i < n; ++i) {
            f[i][i] = A[i];
        }

        for (len = 2; len <= n; ++len) {
            for (i = 0; i <= n - len; ++i) {
                j = i + len - 1;
                f[i][j] = Math.max(A[i] - f[i + 1][j], A[j] - f[i][j - 1]);
            }
        }

        return f[0][n - 1] >= 0;
    }
}

// Linpz verision
public class Solution {
    /**
     * @param values: an array of integers
     * @return: a boolean which equals to true if the first player will win
     */
    public boolean firstWillWin(int[] values) {
        int n = values.length;
        int[] sum = new int[n + 1];
        sum[0] = 0;
        for (int i = 1; i <= n; ++i)
            sum[i] = sum[i - 1] + values[i - 1];

        // s[i][j] = sum[j + 1] - sum[i];

        int[][] dp = new int[n][n];
        for (int i = 0; i < n; ++i)
            dp[i][i] = values[i];

        for (int len = 2; len <= n; ++len) {
            for (int i = 0; i < n; ++i) {
                int j = i + len - 1;
                if (j >= n)
                    continue;
                int s = sum[j + 1] - sum[i];
                dp[i][j] = Math.max(s - dp[i + 1][j], s - dp[i][j - 1]);
            }
        }

        return dp[0][n - 1] > sum[n] / 2;
    }
}
相关推荐
aqiu1111111 小时前
【C++算法打怪专栏】LeetCode 1. 两数之和(哈希表解法总结)
c++·算法·leetcode
fpcc1 小时前
c++编程实践—thread_local变量的应用
c++
2402_882893862 小时前
Linux 进程概念详解(下)—— 进程调度、环境变量与虚拟地址空间
linux·进程·虚拟地址空间
j7~2 小时前
【仿 RabbitMQ 项目】发布订阅消息队列 (篇九):队列数据管理,内存镜像 + SQLite 持久化实现
数据库·c++·分布式·sqlite·rabbitmq·队列数据管理
虚无的纽扣2 小时前
【Linux】揭开文件系统神秘的面纱:第四讲:可执行文件的组装车间
linux·运维·服务器
YMWM_2 小时前
ubuntu系统字体缩放配置
linux·运维·ubuntu
dtq04242 小时前
C++ - 变量
开发语言·c++
傻啦嘿哟2 小时前
天气数据爬虫:基于Scrapy爬取全国天气,做可视化大屏
c++·爬虫·scrapy
Amelio_Ming2 小时前
rk3588 pcie驱动分析之二 (pcie设备树节点分析)
linux·嵌入式硬件