【2024GXOI进阶】分糖果(candy) 博弈论dp
分糖果博弈问题解题思路与代码注释
解题思路
-
问题分析:
-
这是一个双人轮流取糖果的博弈问题
-
两人都采取最优策略,目标是使自己获得的总糖果数最多
-
每次只能取最左边的糖果包,选择给自己或对方
-
分配权会转移到本次未获得糖果的人手中
-
-
关键观察:
-
可以使用动态规划来解决,从后向前计算最优解
-
定义f[i]表示从第i包到第n包糖果时,当前玩家能比对手多获得的最大糖果数
-
状态转移方程:f[i] = max(a[i] - f[i+1], f[i+1] - a[i])
-
第一种情况:当前玩家取第i包,对手从i+1开始能比当前玩家多f[i+1]
-
第二种情况:当前玩家不取第i包,对手取第i包,当前玩家从i+1开始能比对手多f[i+1]
-
-
-
结果计算:
-
总糖果数为sum
-
最终小林比小伊多的糖果数为f[0]
-
小林获得的糖果数为(sum + f[0])/2
-
#include<bits/stdc++.h> #define ll long long // 定义long long类型简写 using namespace std; const int N = 1e5 + 10; // 定义最大糖果包数 int n, a[N], f[N]; // n-糖果包数,a-糖果数量数组,f-DP数组 ll sum; // 所有糖果总数 int main() { cin >> n; // 输入糖果包数 for(int i = 1; i <= n; i++) { cin >> a[i]; // 输入每包糖果数量 sum += a[i]; // 计算总糖果数 } // 从后向前动态规划计算 for(int i = n; i >= 0; i--) { // 状态转移方程: // 当前玩家可以选择取或不取第i包糖果 // 取:a[i] - f[i+1] (获得a[i],但对手在i+1后能比玩家多f[i+1]) // 不取:f[i+1] - a[i] (对手获得a[i],玩家在i+1后能比对手多f[i+1]) f[i] = max(a[i] - f[i + 1], f[i + 1] - a[i]); } // 计算最终结果:(总糖果 + 小林比小伊多的糖果)/2 = 小林获得的糖果 ll ans = (sum + f[0]) / 2; cout << ans; return 0; }
为什么 a[i] - dp[i+1] 是自己拿的净收益?
-
自己拿第
i包:-
当前玩家获得
a[i]糖果。 -
对手没有拿到糖果,因此分配权转移给对手。
-
对手在剩下的
i+1到n包糖果中会采取最优策略,获得比当前玩家多dp[i+1]的糖果(因为dp[i+1]是从i+1开始时的差值,此时对手是“当前玩家”)。 -
因此,当前玩家的净收益是:
自己拿的 �[�]−对手后续的净收益 ��[�+1]自己拿的a[i]−对手后续的净收益dp[i+1] -
即
a[i] - dp[i+1]。
-
-
举例说明:
假设剩余糖果为[5, 7, 9],当前玩家是小林:-
小林拿
5:获得5,分配权转给小伊。 -
小伊面对
[7, 9]时,会通过最优策略获得比小林多dp[i+1]的糖果(假设dp[i+1]=2)。 -
小林的净收益是
5 - 2 = 3。
-
为什么 -a[i] + dp[i+1] 是让对方拿的净收益?
-
让对方拿第
i包:-
对手获得
a[i]糖果。 -
当前玩家没有拿到糖果,因此分配权仍归自己。
-
当前玩家在剩下的
i+1到n包糖果中继续作为“当前玩家”,能获得比对手多dp[i+1]的糖果。 -
因此,当前玩家的净收益是:
-
即
-a[i] + dp[i+1]。
-
-
举例说明:
继续[5, 7, 9]的例子:-
小林让小伊拿
5:小伊获得5,分配权仍归小林。 -
小林面对
[7, 9]时,能通过最优策略获得比小伊多dp[i+1]的糖果(假设dp[i+1]=2)。 -
小林的净收益是
-5 + 2 = -3(此时小伊总收益更多)。
-
为什么取 max?
当前玩家会选择对自己更有利的策略:
-
自己拿:净收益
a[i] - dp[i+1]。 -
对方拿:净收益
-a[i] + dp[i+1]。
选择两者中的最大值,即:
dp[i]=max(a[i]−dp[i+1],−a[i]+dp[i+1])
为什么需要倒序遍历动态规划数组 dp?
在解决这个分糖果的博弈问题时,我们使用动态规划(DP)来记录从第 i 包到第 n 包糖果时,当前拥有分配权的玩家能比对手多获得的糖果数。关键在于理解 dp[i] 依赖于 dp[i+1],即当前状态依赖于后续状态。以下是详细解释:
1. 依赖关系:当前状态需要知道后续状态的结果
-
定义:
dp[i]表示从第i包到第n包糖果时,当前玩家的最大净收益。 -
依赖:为了计算
dp[i],我们需要知道dp[i+1](即从第i+1包到第n包的结果),因为当前玩家的选择会影响后续的分配权归属和收益。-
如果自己拿第
i包,对手会在i+1到n包中采取最优策略(即dp[i+1]是对手的净收益)。 -
如果让对方拿第
i包,自己会在i+1到n包中继续作为当前玩家(即dp[i+1]是自己的净收益)。
-
-
结论:
dp[i]的计算必须基于dp[i+1],因此需要先计算dp[i+1],再计算dp[i]。这就是倒序遍历的原因。 -

浙公网安备 33010602011771号