题目描述
Alberto\texttt{Alberto}Alberto 和 Wanderley\texttt{Wanderley}Wanderley 玩一个卡牌游戏。桌上有 NNN 张牌(NNN 为偶数),每张牌上有一个整数,排成一列。两人轮流取牌,Alberto\texttt{Alberto}Alberto 先手,每次只能从当前序列的两端(最左或最右)取一张牌,直到所有牌被取完。玩家的得分是他取到的牌上数字之和。Alberto\texttt{Alberto}Alberto 希望最大化自己的得分,Wanderley\texttt{Wanderley}Wanderley 则希望最小化 Alberto\texttt{Alberto}Alberto 的得分。两人都采取最优策略。给定牌的序列,求 Alberto\texttt{Alberto}Alberto 能获得的最大分数。
输入格式
- 多个测试用例
- 每个测试用例两行:
- 第一行:整数 NNN(牌的数量)
- 第二行:NNN 个整数,表示牌上的数字
输出格式
- 每个测试用例一行,输出 Alberto\texttt{Alberto}Alberto 能得到的最大分数
限制条件
- 2≤N≤1042 \leq N \leq 10^42≤N≤104
- NNN 是偶数
- 每个整数可以用 323232 位有符号整数表示
题目分析
这是一个典型的零和博弈问题,具有以下特点:
- 回合交替:Alberto\texttt{Alberto}Alberto 先手,Wanderley\texttt{Wanderley}Wanderley 后手,两人轮流取牌
- 有限选择:每回合只能从当前序列的两端取牌
- 完全信息:双方都知道所有牌的数字
- 最优策略:双方都采取对自己最有利的策略
由于 NNN 最大可达 10410^4104,我们需要一个高效的算法来解决这个问题。
解题思路
动态规划定义
我们使用动态规划来解决这个问题。定义状态:
- dp[i][j]dp[i][j]dp[i][j] 表示当牌堆剩下第 iii 张到第 jjj 张牌时,当前轮到的人能比对手多得的分数(当前玩家分数 −-− 对手分数)
状态转移
关键点在于判断当前是谁的回合。由于初始时 NNN 是偶数且 Alberto\texttt{Alberto}Alberto 先手:
- 当剩余牌数 (j−i+1)(j-i+1)(j−i+1) 为偶数时,是 Alberto\texttt{Alberto}Alberto 的回合
- 当剩余牌数 (j−i+1)(j-i+1)(j−i+1) 为奇数时,是 Wanderley\texttt{Wanderley}Wanderley 的回合
状态转移方程:
-
Alberto\texttt{Alberto}Alberto 的回合(最大化自己的优势):
dp[i][j]=max(cards[i]+dp[i+1][j], cards[j]+dp[i][j−1]) dp[i][j] = \max(cards[i] + dp[i+1][j],\ cards[j] + dp[i][j-1]) dp[i][j]=max(cards[i]+dp[i+1][j], cards[j]+dp[i][j−1]) -
Wanderley\texttt{Wanderley}Wanderley 的回合(最小化 Alberto\texttt{Alberto}Alberto 的优势):
dp[i][j]=min(−cards[i]+dp[i+1][j], −cards[j]+dp[i][j−1]) dp[i][j] = \min(-cards[i] + dp[i+1][j],\ -cards[j] + dp[i][j-1]) dp[i][j]=min(−cards[i]+dp[i+1][j], −cards[j]+dp[i][j−1])
空间优化
由于 NNN 最大为 10410^4104,二维 DP\texttt{DP}DP 数组需要约 10810^8108 个元素,可能超出内存限制。我们可以优化为一维 DP\texttt{DP}DP:
- 使用 dp[i]dp[i]dp[i] 表示从位置 iii 开始,当前长度为 lenlenlen 的区间中,当前玩家能比对手多得的最大分数差
- 按区间长度从小到大计算
最终得分计算
设总分为 total=∑k=0N−1cards[k]total = \sum_{k=0}^{N-1} cards[k]total=∑k=0N−1cards[k],最终 dp[0]dp[0]dp[0] 表示 Alberto\texttt{Alberto}Alberto 比 Wanderley\texttt{Wanderley}Wanderley 多得的分数。那么:
- Alberto\texttt{Alberto}Alberto 得分 AAA,Wanderley\texttt{Wanderley}Wanderley 得分 BBB
- A+B=totalA + B = totalA+B=total
- A−B=dp[0]A - B = dp[0]A−B=dp[0]
解得:
A=total+dp[0]2
A = \frac{total + dp[0]}{2}
A=2total+dp[0]
算法复杂度
- 时间复杂度:O(N2)O(N^2)O(N2)
- 空间复杂度:O(N)O(N)O(N)
对于 N=104N = 10^4N=104,时间复杂度为 10810^8108 级别,在现代计算机上可以接受。
代码实现
// Cards
// UVa ID: 12484
// Verdict: Accepted
// Submission Date: 2025-11-16
// UVa Run Time: 0.150s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
while (cin >> n) {
vector<int> cards(n);
for (int i = 0; i < n; i++)
cin >> cards[i];
vector<long long> dp(n, 0);
for (int len = 1; len <= n; len++)
for (int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
if ((n - len) % 2 == 0)
// Alberto's turn - maximize
if (len == 1)
dp[i] = cards[i];
else
dp[i] = max(cards[i] + dp[i+1], cards[j] + dp[i]);
else
// Wanderley's turn - minimize Alberto's score
if (len == 1)
dp[i] = -cards[i];
else
dp[i] = min(-cards[i] + dp[i+1], -cards[j] + dp[i]);
}
long long total = 0;
for (int x : cards) total += x;
long long $\texttt{Alberto}$Score = (total + dp[0]) / 2;
cout << $\texttt{Alberto}$Score << endl;
}
return 0;
}
总结
本题通过动态规划巧妙地模拟了双人博弈过程,关键点在于:
- 正确识别当前回合的玩家
- Alberto\texttt{Alberto}Alberto 采取最大化策略,Wanderley\texttt{Wanderley}Wanderley 采取最小化策略
- 使用空间优化避免内存溢出
- 最终通过总分和分数差计算 Alberto\texttt{Alberto}Alberto 的实际得分
这种"当前玩家相对于对手的分数差"的思路是解决零和博弈问题的常用技巧,可以推广到类似的游戏中。
614

被折叠的 条评论
为什么被折叠?



