美文网首页刷题集
博弈型动态规划

博弈型动态规划

作者: 6默默Welsh | 来源:发表于2019-09-30 10:50 被阅读0次

博弈类问题的套路都差不多,下文举例讲解,其核心思路是在二维 dp 的基础上使用元组分别存储两个人的博弈结果。掌握了这个技巧以后,别人再问你什么俩海盗分宝石,俩人拿硬币的问题,你就告诉别人:我懒得想,直接给你写个算法算一下得了。

举个例子:

你和你的朋友面前有一排石头堆,用一个数组 piles 表示,piles[i] 表示第 i 堆石子有多少个。你们轮流拿石头,一次拿一堆,但是只能拿走最左边或者最右边的石头堆。所有石头被拿完后,谁拥有的石头多,谁获胜。

石头的堆数可以是任意正整数,石头的总数也可以是任意正整数,这样就能打破先手必胜的局面了。比如有三堆石头 piles = [1,100,3],先手不管拿 1 还是 3,能够决定胜负的 100 都会被后手拿走,后手会获胜。

假设两人都很聪明,请你设计一个算法,返回先手和后手的最后得分(石头总数)之差。比如上面那个例子,先手能获得 4 分,后手会获得 100 分,你的算法应该返回 -96。

这样推广之后,这个问题算是一道 Hard 的动态规划问题了。博弈问题的难点在于,两个人要轮流进行选择,而且都贼精明,应该如何编程表示这个过程呢?

一、定义 dp 数组的含义

定义 dp 数组的含义是很有技术含量的,同一问题可能有多种定义方法,不同的定义会引出不同的状态转移方程,不过只要逻辑没有问题,最终都能得到相同的答案。

我建议不要迷恋那些看起来很牛逼,代码很短小的奇技淫巧,最好是稳一点,采取可解释性最好,最容易推广的设计思路。本文就给出一种博弈问题的通用设计框架。

介绍 dp 数组的含义之前,我们先看一下 dp 数组最终的样子:


image.png

下文讲解时,认为元组是包含 first 和 second 属性的一个类,而且为了节省篇幅,将这两个属性简写为 fir 和 sec。比如按上图的数据,我们说 dp[1][3].fir = 10,dp[0][1].sec = 3。

先回答几个读者可能提出的问题:

这个二维 dp table 中存储的是元组,怎么编程表示呢?这个 dp table 有一半根本没用上,怎么优化?很简单,都不要管,先把解题的思路想明白了再谈也不迟。

以下是对 dp 数组含义的解释:

image.png

我们想求的答案是先手和后手最终分数之差,按照这个定义也就是 dp[0][n−1].fir−dp[0][n−1].sec

二、状态转移方程

写状态转移方程很简单,首先要找到所有「状态」和每个状态可以做的「选择」,然后择优。

根据前面对 dp 数组的定义,状态显然有三个:****开始的索引 i,结束的索引 j,当前轮到的人。

dp[i][j][fir or sec]
其中:
0 <= i < piles.lengthi <= j < piles.length
其中:
0 <= i < piles.lengthi <= j < piles.length

对于这个问题的每个状态,可以做的选择有两个:****选择最左边的那堆石头,或者选择最右边的那堆石头。 我们可以这样穷举所有状态:

image.png
上面的伪码是动态规划的一个大致的框架,股票系列问题中也有类似的伪码。这道题的难点在于,两人是交替进行选择的,也就是说先手的选择会对后手有影响,这怎么表达出来呢?

根据我们对 dp 数组的定义,很容易解决这个难点,写出状态转移方程:


image.png

根据 dp 数组的定义,我们也可以找出 base case,也就是最简单的情况:


image.png
image.png
这里需要注意一点,我们发现 base case 是斜着的,而且我们推算 dp[i][j] 时需要用到 dp[i+1][j] 和 dp[i][j-1]:
image.png

所以说算法不能简单的一行一行遍历 dp 数组,而要斜着遍历数组


image.png
说实话,斜着遍历二维数组说起来容易,你还真不一定能想出来怎么实现,不信你思考一下?这么巧妙的状态转移方程都列出来了,要是不会写代码实现,那真的很尴尬了。

三、代码实现

import java.util.Scanner;
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int N = in.nextInt();
        int[] nums = new int[N];
        for (int i = 0; i < N; i++) {
            nums[i] = in.nextInt();
        }
        dp_deal(nums);
    }

    static class Pair {
        int fir, sec;
        Pair(int fir, int sec) {
            this.fir = fir;
            this.sec = sec;
        }
    }

    public static void dp_deal(int[] nums) {
        int n = nums.length;
        Pair[][] dp = new Pair[n][n];

        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                dp[i][j] = new Pair(0, 0);
            }
        }
        for (int i = 0; i < n; i++) {
            dp[i][i].fir = nums[i];
            dp[i][i].sec = 0;
        }

        for (int l = 1; l <= n-1; l++) {
            for (int i = 0; i < n-1; i++) {
                int j = l + i;
                if (j >= n) {
                    break;
                }
                int left = nums[i] + dp[i+1][j].sec;
                int right = nums[j] + dp[i][j-1].sec;
                if (left > right) {
                    dp[i][j].fir = left;
                    dp[i][j].sec = dp[i+1][j].fir;
                } else {
                    dp[i][j].fir = right;
                    dp[i][j].sec = dp[i][j-1].fir;
                }
            }
        }

        Pair res = dp[0][n-1];
        int num = Math.max(res.fir, res.sec);
        System.out.println(num);
    }
}

动态规划解法,如果没有状态转移方程指导,绝对是一头雾水,但是根据前面的详细解释,读者应该可以清晰理解这一大段代码的含义。

而且,注意到计算 dp[i][j] 只依赖其左边和下边的元素,所以说肯定有优化空间,转换成一维 dp,想象一下把二维平面压扁,也就是投影到一维。但是,一维 dp 比较复杂,可解释性很差,大家就不必浪费这个时间去理解了。

四、最后总结

本文给出了解决博弈问题的动态规划解法。博弈问题的前提一般都是在两个聪明人之间进行,编程描述这种游戏的一般方法是二维 dp 数组,数组中通过元组分别表示两人的最优决策。

之所以这样设计,是因为先手在做出选择之后,就成了后手,后手在对方做完选择后,就变成了先手。这种角色转换使得我们可以重用之前的结果,典型的动态规划标志。

相关文章

  • 博弈型动态规划

    博弈类问题的套路都差不多,下文举例讲解,其核心思路是在二维 dp 的基础上使用元组分别存储两个人的博弈结果。掌握了...

  • 02-16:动态规划题总结

    1、动态规划解除数博弈 1025. 除数博弈[https://leetcode-cn.com/problems/d...

  • 动态规划

    dp可以解决的问题 (1)最值(2)方案数 (3)可行性dp的方向性 :坐标型动态规划,前缀型动态规划dp[坐标...

  • 动态博弈

    动态博弈 动态博弈的本质不是轮流出招,而是去改变游戏的规则。 现实中的博弈都是动态博弈,你的行为也影响对方的行为。...

  • LeetCode—— 不同路径

    题目描述 一、CPP 1. 动态规划法: 解题思路:详解异步动态规划笔记——类型二:计数型。 时间复杂度:O(n2...

  • 动态规划解决博弈问题(1)

    今天听了北美培训机构九章算法的一节公开课,做点心得笔录。 1.首先介绍什么是博弈问题,面试中博弈问题的特点 生活中...

  • 2020-02-09 DP - 2 - 坐标型DP & 划分型D

    坐标型动态规划: 数组下标[i][j]就是坐标(i, j) Leetcode 256 - Paint HouseT...

  • Algorithm进阶计划 -- 动态规划(上)

    动态规划动态规划的基本原理动态规划的运用 1. 动态规划的基本原理 动态规划(Dynamic Programmi...

  • 通信工程导论(13)

    网络的动态性引起不确定性 随机过程,图论,概率论,博弈论,线性规划,凸优化,微分方程整合起来后,完成如何应对动态性...

  • 4. 动态规划算法

    1. 动态规划算法总结2. 漫画:什么是动态规划?3.算法之动态规划4. 动态规划-算法

网友评论

    本文标题:博弈型动态规划

    本文链接:https://www.haomeiwen.com/subject/kgecpctx.html