九章算法笔记D4-博弈型动态规划

本文深入探讨了博弈型动态规划的两大经典问题:Coins in a Line 和 Coins in a Line III,详细讲解了如何通过动态规划解决博弈论中的先手优势问题,提供了清晰的代码实现。

tags:

  • 算法
  • DP
  • 动态规划

大家觉得写还可以,可以点赞、收藏、关注一下吧!
也可以到我的个人博客参观一下,估计近几年都会一直更新!和我做个朋友吧!https://motongxue.cn


D4 博弈型动态规划

Coins in a Line

Problems:

n 个硬币排成一条线。两个参赛者轮流从右边依次拿走 1 或 2 个硬币, 直到没有硬币为止。拿到最后一枚硬币的人获胜。

请判定 先手玩家 必胜还是必败?

若必胜, 返回 true, 否则返回 false.

样例 :

输入: 4
输出: true
解释: 
先手玩家第一轮拿走一个硬币, 此时还剩三个.
这时无论后手玩家拿一个还是两个, 下一次先手玩家都可以把剩下的硬币拿完.

确定状态:

博弈型动态规划通常从第一步 分析,而不是最后一步(因为局面越来越简单,硬币数越来越少)

知识点:

  1. 如果取1个或2个硬币后,能让剩下的局面先手必败,则当前先手必胜
  2. 如果不管怎么走,剩下的局面都是先手必胜,则当前先手必败
  3. 总之:
    • 必胜: 在当下的局面走出一步,让对手无路可逃
    • 必败: 自己无路可逃
public boolean firstWillWin(int n) {
  if (n == 0)
    return false;
  if (n == 1)
    return true;
  boolean[] f = new boolean[n + 1];
  int i;
  f[0] = false;
  f[1] = true;
  for (i = 2; i <= n; i++) {
    f[i] = (!f[i - 1]) || (!f[i - 2]);
  }
  /*可以证明, 当硬币数目是3的倍数的时候, 先手玩家必败, 否则他必胜.
      当硬币数目是3的倍数时, 每一轮先手者拿a个, 后手者拿3-a个即可, 后手必胜.
      若不是3的倍数, 先手者可以拿1或2个, 此时剩余硬币个数就变成了3的倍数
        */
  return f[n];
}

Coins in a Line III

Problem

There are n coins in a line. Two players take turns to take a coin from one of the ends of the line until there are no more coins left. The player with the larger amount of money wins.

Could you please decide the first player will win or lose?

Example

Given array A = [3,2,2], return true.
Given array A = [1,2,4], return true.
Given array A = [1,20,4], return false.

分析

设己方数字和是A,对手数字和是B,即目标是A>=B,由于记录两个变量的状态太复杂,所以还是把两个选手当成一个人,每次面对a[i…j]选最优解。这里dp[i][j]是当前选手对a[i…j]所选的coins与对手将要选的coins的最大差值。

初始条件dp[i][i] = values[i]。因为只有1个coin,肯定是全选。

转移方程f[i][j] = max{a[i] - f[i+1][j], a[j] - f[i][j-1]}

  1. f[i][j]:为一方先手在面对a[i...j]这些数字时,能得到的最大的与对手的数字差
  2. a[i] - f[i+1][j]:选择a[i],对手采取最优策略时自己能得到的最大的与对手的数字差
  3. a[j] - f[i][j-1]:选择a[j],对手采取最优策略时自己能得到的最大的与对手的数字差

计算顺序

f[0][0], f[1][1], f[2][2], …, f[n-1][n-1] //len = 0, 1 coin
f[0][1], …, f[n-2][n-1] //len = 1, 2 coins

f[0][n-1] //len = n - 1, n coins

public class Solution{
    public boolean firstWillWin(int[] A){
    	int n = A.length;
        int[][] f = new int[n][n];
        int i,j,len;
        for(i=0; i < n; i++)	f[i][i] = A[i];
        for(len = 2; len <= n; len++)
            for(i = 0; i <= n; 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;
	}
}

2020年7月9日更


大家觉得写还可以,可以点赞、收藏、关注一下吧!
也可以到我的个人博客参观一下,估计近几年都会一直更新!和我做个朋友吧!https://motongxue.cn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值