题解:AtCoder AT_awc0063_d Card Taking Game
【题目来源】
AtCoder:D - Card Taking Game
【题目描述】
Takahashi and Aoki play a game using cards.
高桥和青木用卡片玩一个游戏。
\(N\) cards are arranged in a row on the table, and the \(i\)-th card from the left has an integer \(V_i\) written on it. Note that \(V_i\) may be negative.
桌上有 \(N\) 张卡片排成一行,从左数第 \(i\) 张卡片上写有一个整数 \(V_i\)。注意 \(V_i\) 可能是负数。
The two players take cards according to the following rules:
两名玩家按照以下规则取卡片:
- Takahashi goes first, Aoki goes second, and they take turns alternately.
高桥先手,青木后手,他们交替进行回合。 - On their turn, a player chooses exactly one card from either the left end or the right end of the row of cards remaining on the table, takes it, and places it in their hand. The taken card is removed from the table, and the order of the remaining cards does not change.
在自己的回合,玩家从桌上剩余的卡片行的左端或右端选择恰好一张卡片,取走并放入自己手中。被取走的卡片从桌上移除,剩余卡片的顺序不变。 - The game ends when all cards have been taken.
当所有卡片都被取走时,游戏结束。
Both players know all information including each other's strategies, and each plays optimally to maximize the sum of the integers written on the cards they have taken.
双方玩家都知道所有信息,包括对方的策略,并且每人都采取最优策略,以最大化自己取走卡片上整数之和。
When both players play optimally, find the value obtained by subtracting the sum of the integers written on the cards Aoki took from the sum of the integers written on the cards Takahashi took.
当双方都采取最优策略时,求高桥取走卡片上整数之和减去青木取走卡片上整数之和得到的差值。
【输入】
\(N\)
\(V_1\) \(V_2\) \(\ldots\) \(V_N\)
- The first line contains an integer \(N\) representing the number of cards.
- The second line contains the integers \(V_1, V_2, \ldots, V_N\) written on each card, separated by spaces. Here, \(V_i\) is the integer written on the \(i\)-th card from the left.
【输出】
Print in one line the value obtained by subtracting the sum of the integers written on the cards Aoki took from the sum of the integers written on the cards Takahashi took, when both players play optimally.
【输入样例】
4
3 1 2 5
【输出样例】
3
【核心思想】
-
问题分析:桌上有 \(N\) 张卡片排成一行,每张卡片有整数 \(V_i\)(可为负数)。高桥先手,两人轮流从剩余卡片的左端或右端取走一张。双方都采取最优策略以最大化自己手中卡片之和。求高桥得分减去青木得分的差值。这是一个经典的双人博弈区间 DP问题,可用 minimax 思想将双方最优策略转化为单状态的最优子结构。
-
算法选择:
- 区间 DP( minimax ):定义 \(dp[i][j]\) 为在子区间 \([i, j]\) 上,当前行动玩家能获得的最大得分差值(当前玩家得分 \(-\) 对手得分)
- 差值转化:不分别记录双方得分,而是直接维护"当前玩家 \(-\) 对手"的差值,使状态转移简洁对称
-
关键步骤:
- 初始化:\(dp[i][i] = V_i\)(区间只剩一张牌时,当前玩家直接取走)
- 枚举区间长度 \(\text{len}\) 从 \(2\) 到 \(N\)
- 枚举区间起点 \(i\),计算终点 \(j = i + \text{len} - 1\)
- 状态转移:
- \(dp[i][j] = \max(V_i - dp[i+1][j],\; V_j - dp[i][j-1])\)
- 取左端 \(V_i\):当前玩家获得 \(V_i\),但对手在剩余区间 \([i+1, j]\) 上能获得 \(dp[i+1][j]\) 的差值(对手 \(-\) 当前玩家),所以当前净收益为 \(V_i - dp[i+1][j]\)
- 取右端 \(V_j\):同理,净收益为 \(V_j - dp[i][j-1]\)
- 输出答案:\(dp[1][N]\)(先手高桥在完整区间上的最优差值)
-
时间/空间复杂度:
- 时间复杂度:\(O(N^2)\),枚举所有 \(O(N^2)\) 个区间,每个区间 \(O(1)\) 转移
- 空间复杂度:\(O(N^2)\),二维 DP 数组
-
区间博弈 DP 的核心思想:
- 差值 DP:将"双方得分"转化为"当前玩家 \(-\) 对手"的单一差值,避免维护两个状态,是零和博弈 DP 的经典技巧
- 最优子结构:当前玩家的最优选择仅依赖于更小规模子区间的最优结果,具有无后效性
- minimax 自洽性:\(dp[i][j]\) 定义为"当前玩家最优差值",转移到子区间时对手视角自然反转,无需额外标记轮到谁
- 适用于双人轮流取物、区间两端选择、最优策略博弈类问题(如石子游戏、卡牌博弈等)
【算法标签】
区间DP
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 定义int为long long类型
const int N = 3005; // 最大数组长度
int n; // 数组长度
int v[N]; // 数组元素
int dp[N][N]; // 动态规划表
signed main()
{
cin >> n; // 输入数组长度
for (int i = 1; i <= n; i++)
{
cin >> v[i]; // 输入数组元素
}
// 初始化:单个元素的子数组
for (int i = 1; i <= n; i++)
{
dp[i][i] = v[i]; // 当只有一个元素时,先手得分为v[i]
}
// 区间DP:从长度2开始
for (int len = 2; len <= n; len++) // 枚举区间长度
{
for (int i = 1; i + len - 1 <= n; i++) // 枚举区间起点
{
int j = i + len - 1; // 区间终点
// 状态转移:当前玩家在区间[i,j]上的最大得分差值
// 选择左端点:得分 = v[i] - 对手在剩余区间[i+1,j]的最大得分差值
// 选择右端点:得分 = v[j] - 对手在剩余区间[i,j-1]的最大得分差值
dp[i][j] = max(v[i] - dp[i + 1][j], v[j] - dp[i][j - 1]);
}
}
cout << dp[1][n] << endl; // 输出先手在完整区间[1,n]上的最大得分差值
return 0; // 程序正常结束
}
【运行结果】
4
3 1 2 5
3
浙公网安备 33010602011771号