2023年12月GESP真题及题解(C++七级): 纸牌游戏

2023年12月GESP真题及题解(C++七级): 纸牌游戏

题目描述

你和小杨在玩一个纸牌游戏。

你和小杨各有 3 3 3 张牌,分别是 0 、 1 、 2 0、1、2 0、1、2。你们要进行 N N N 轮游戏,每轮游戏双方都要出一张牌,并按 1 1 1 战胜 0 0 0, 2 2 2 战胜 1 1 1, 0 0 0 战胜 2 2 2 的规则决出胜负。第 i i i 轮的胜者可以获得 2 × a i 2 \times a_i 2×ai 分,败者不得分,如果双方出牌相同,则算平局,二人都可获得 a i a_i ai 分 ( i = 1 , 2 , ⋯   , N ) (i=1,2,\cdots,N) (i=1,2,⋯,N)。

玩了一会后,你们觉得这样太过于单调,于是双方给自己制定了不同的新规则。小杨会在整局游戏开始前确定自己全部 n n n 轮的出牌,并将他的全部计划告诉你;而你从第 2 2 2 轮开始,要么继续出上一轮出的牌,要么记一次"换牌"。游戏结束时,你换了 t t t 次牌,就要额外扣 b 1 + ⋯ + b t b_1+\cdots+b_t b1+⋯+bt 分。

请计算出你最多能获得多少分。

输入格式

第一行一个整数 N N N,表示游戏轮数。

第二行 N N N 个用单个空格隔开的非负整数 a 1 , ⋯   , a N a_1,\cdots,a_N a1,⋯,aN,意义见题目描述。

第三行 N − 1 N-1 N−1 个用单个空格隔开的非负整数 b 1 , ⋯   , b N − 1 b_1,\cdots,b_{N-1} b1,⋯,bN−1,表示换牌的罚分,具体含义见题目描述。由于游戏进行 N 轮,所以你至多可以换 N − 1 N-1 N−1 次牌。

第四行 N N N 个用单个空格隔开的整数 c 1 , ⋯   , c N c_1,\cdots,c_N c1,⋯,cN,依次表示小杨从第 1 1 1 轮至第 N N N 轮出的牌。保证 c i ∈ 0 , 1 , 2 c _i\in{0,1,2} ci∈0,1,2。

输出格式

一行一个整数,表示你最多获得的分数。

输入输出样例 1
输入 1
复制代码
4
1 2 10 100
1 100 1
1 1 2 0
输出 1
复制代码
219
输入输出样例 2
输入 2
复制代码
6
3 7 2 8 9 4
1 3 9 27 81
0 1 2 1 2 0
输出 2
复制代码
56
说明/提示

样例解释 1

你可以第 1 1 1 轮出 0 0 0,并在第 2 , 3 2,3 2,3 轮保持不变,如此输掉第 1 , 2 1,2 1,2 轮,但在第 3 3 3 轮中取胜,获得 2 × 10 = 20 2×10=20 2×10=20 分;

随后,你可以在第 4 4 4 轮中以扣 1 1 1 分为代价改出 1 1 1 ,并在第 4 4 4 轮中取得胜利,获得 2 × 100 = 200 2×100=200 2×100=200 分。

如此,你可以获得最高的总分 20 + 200 − 1 = 219 20+200-1=219 20+200−1=219。

数据范围

对于 30 % 30\% 30% 的测试点,保证 N ≤ 15 N\le15 N≤15。

对于 60 % 60\% 60% 的测试点,保证 N ≤ 100 N\le100 N≤100。

对于所有测试点,保证 N ≤ 1 , 000 N \le 1,000 N≤1,000;保证 0 ≤ a i , b i ≤ 10 6 0 \le a_i,b_i \le 10^6 0≤ai,bi≤106。

思路分析

这是一个动态规划问题。我们需要在已知对手所有出牌的情况下,选择自己的出牌序列,使得总得分最大化,同时考虑换牌的惩罚成本。

核心思路
  1. 状态定义

    • f[i][k][s] 表示进行了前 i 轮游戏,换了 k 次牌,且第 i 轮出牌为 s (0,1,2) 时的最大得分。
  2. 状态转移

    • 如果第 i 轮出牌与第 i-1 轮相同:f[i][k][s] = max(f[i][k][s], f[i-1][k][s] + getScore(i, s))
    • 如果第 i 轮出牌与第 i-1 轮不同:f[i][k][s] = max(f[i][k][s], f[i-1][k-1][t] + getScore(i, s) - b[k])
      其中 t 是上一轮的出牌,b[k] 是第 k 次换牌的惩罚分
  3. 得分计算

    • 根据题目规则计算每轮得分:平局得 a[i],胜利得 2*a[i],失败得 0
  4. 初始化

    • 第一轮没有换牌,直接计算每种出牌的得分
  5. 答案

    • 所有 f[N][k][s] 中的最大值
时间复杂度
  • 状态数:O(N * N * 3) = O(3N²)
  • 转移:每个状态最多从 3 个状态转移过来
  • 总复杂度:O(9N²),对于 N ≤ 1000 可以接受

代码实现

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
const long long INF = 1e18;

int n;
int a[N], b[N], c[N];
long long f[N][N][3];  // f[i][k][s]: 前i轮,换k次牌,第i轮出牌为s的最大得分

// 计算第i轮出牌s的得分(不考虑换牌惩罚)
int getScore(int i, int s) {
    if (s == c[i]) return a[i];  // 平局
    if ((s == 1 && c[i] == 0) || (s == 2 && c[i] == 1) || (s == 0 && c[i] == 2)) {
        return 2 * a[i];  // 胜利
    }
    return 0;  // 失败
}

int main() {
    // 输入
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i < n; i++) cin >> b[i];
    for (int i = 1; i <= n; i++) cin >> c[i];
    
    // 初始化
    for (int i = 0; i <= n; i++) {
        for (int k = 0; k <= n; k++) {
            for (int s = 0; s < 3; s++) {
                f[i][k][s] = -INF;
            }
        }
    }
    
    // 第一轮:没有换牌,直接计算得分
    for (int s = 0; s < 3; s++) {
        f[1][0][s] = getScore(1, s);
    }
    
    // 动态规划
    for (int i = 2; i <= n; i++) {
        for (int k = 0; k < i; k++) {  // 最多换i-1次牌
            for (int s = 0; s < 3; s++) {  // 当前出牌
                // 情况1:不出牌不变(不换牌)
                if (f[i-1][k][s] != -INF) {
                    f[i][k][s] = max(f[i][k][s], f[i-1][k][s] + getScore(i, s));
                }
                
                // 情况2:出牌改变(换牌),需要k>0
                if (k > 0) {
                    for (int t = 0; t < 3; t++) {  // 上一轮出牌
                        if (t != s && f[i-1][k-1][t] != -INF) {
                            f[i][k][s] = max(f[i][k][s], f[i-1][k-1][t] + getScore(i, s) - b[k]);
                        }
                    }
                }
            }
        }
    }
    
    // 输出答案:所有可能状态的最大值
    long long ans = -INF;
    for (int k = 0; k < n; k++) {
        for (int s = 0; s < 3; s++) {
            ans = max(ans, f[n][k][s]);
        }
    }
    cout << ans << endl;
    
    return 0;
}

功能分析

1. 数据存储
  • a[i]: 第 i 轮的基础得分
  • b[i]: 第 i 次换牌的惩罚分
  • c[i]: 对手在第 i 轮的出牌
  • f[i][k][s]: DP 状态数组
2. 状态设计
  • 三维 DP:轮数 × 换牌次数 × 当前出牌
  • 使用 -INF 初始化表示不可达状态
3. 得分计算函数
  • 根据游戏规则(剪刀石头布变种)计算得分
  • 平局:a[i]
  • 胜利:2*a[i]
  • 失败:0 分
4. 转移方程实现
  • 不换牌:直接从相同出牌状态转移,不扣分
  • 换牌 :从不同出牌状态转移,扣除 b[k]
  • 注意换牌次数 k 的限制
5. 边界处理
  • 第一轮特殊处理:没有上一轮,不涉及换牌
  • 换牌次数不超过轮数减一
  • 使用 -INF 避免非法状态转移

完整GESP C++考级真题题解专栏:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html

更多csp信奥赛C++学习资料汇总:

1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):

https://edu.csdn.net/lecturer/7901 点击跳转


2、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html

4、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
Trouvaille ~1 小时前
【Linux】进程间通信(一):IPC基础与管道机制深度剖析
linux·运维·c++·管道·进程间通信·匿名管道·半双工
REDcker2 小时前
libwebsockets完整文档
c++·后端·websocket·后端开发·libwebsockets
Sheep Shaun2 小时前
深入理解红黑树:从概念到完整C++实现详解
java·开发语言·数据结构·c++·b树·算法
楼田莉子2 小时前
CMake学习:入门及其下载配置
开发语言·c++·vscode·后端·学习
夜月yeyue2 小时前
VFS (虚拟文件系统) 核心架构
linux·c++·单片机·嵌入式硬件·架构
旭意2 小时前
数据结构-红黑树和set
数据结构·c++·算法·蓝桥杯
无小道2 小时前
基于epoll的单进程Reactor服务器
运维·服务器·c++·网络编程·reactor·epoll
CSDN_RTKLIB3 小时前
对象类型转换与引用类型转换
c++
是娇娇公主~3 小时前
C++集群聊天服务器(4)——网络模块与业务模块
服务器·网络·c++