#P14772. [Bulgarian2025组队赛]Cardgame(要多线程来测,暂时不处理)
[Bulgarian2025组队赛]Cardgame(要多线程来测,暂时不处理)
题目类型说明
这是一道提交函数题 / 多进程交互库调用题。你需要实现题目要求的函数,由评测程序在不同进程中调用。
你需要实现两个函数:
int giveHint(int N, int M, int H, std::vector<int> cards);
void playGame(
int N,
int M,
int H,
int playerIndex,
std::vector<int> cards,
int hint,
std::vector<int> earlierPlaced
);
评测程序还会提供以下接口供 playGame 调用:
std::vector<int> passTurn();
void placeCard();
题目描述
Klimi 邀请了 N 位朋友来体验她新设计的一款卡牌游戏,朋友们按编号 围坐在一张圆桌旁。
游戏开始时,每位玩家都会抽到一张写有整数的卡牌,数值范围在区间
内。卡牌会被放在玩家额头上,因此:
- 每位玩家都能看到其他所有玩家的卡牌;
- 但看不到自己的卡牌。
牌堆中每个数值的卡牌数量都无限,因此允许多位玩家抽到相同的数值。
游戏过程中,玩家之间不能以任何方式通信。
游戏规则
游戏按**轮(round)**进行。每一轮中,仍未出牌的玩家按照编号从小到大的顺序依次行动。
当某位仍在游戏中的玩家行动时,他有两种选择:
- Pass:本轮不做任何操作;
- Place card:把自己的卡牌放到桌上,并从此退出游戏。
如果某位玩家已经放下了卡牌,那么之后的轮次中会跳过他的行动。
游戏会持续若干轮,直到所有玩家都把卡牌放到桌上。
游戏的目标是:桌上的卡牌按非递减顺序出现。
Klimi 的提示
由于这是朋友们第一次玩这款游戏,Klimi 担心他们太难获胜。因此,在所有人抽牌完成后,Klimi 会看到所有玩家的卡牌,然后公开说出一个提示。这个提示只能是一个整数,范围为:
此外,Klimi 希望早点睡觉,所以整局游戏不能超过 轮。具体取值见后文的“子任务”和“评分方式”。
你的任务是为:
- Klimi 设计生成提示的方法;
- 为所有玩家设计出牌策略,
使得游戏在限制内成功完成。
实现细节
你需要实现两个函数。
1. giveHint
int giveHint(int N, int M, int H, std::vector<int> cards);
该函数表示 Klimi 的行为,负责生成公开提示。
参数说明:
N、M、H:题目中给定的参数;cards[i]:第i位玩家抽到的卡牌数值。
返回值必须在区间
内。
2. playGame
void playGame(
int N,
int M,
int H,
int playerIndex,
std::vector<int> cards,
int hint,
std::vector<int> earlierPlaced
);
该函数表示某个玩家视角下的策略。
参数说明:
N、M、H:题目中给定的参数;playerIndex:当前玩家编号;cards:所有玩家的卡牌信息。- 对于 ,
cards[i]等于第i位玩家的卡牌值; - 对于当前玩家本人,有
cards[playerIndex] = -1;
- 对于 ,
hint:giveHint生成的提示;earlierPlaced:在第一轮中,编号比playerIndex小、并且已经放下卡牌的玩家编号列表。
在 playGame 中,玩家必须通过 grader 提供的接口进行行动。
如果当前玩家选择继续等待,应调用:
std::vector<int> passTurn();
这会让系统继续模拟其他玩家的行动,直到下一轮该玩家再次轮到行动时才返回。返回值为:在这段时间内已经放下卡牌的玩家编号列表。
如果当前玩家决定出牌,应调用:
void placeCard();
调用 placeCard() 之后,你的程序:
- 不得再调用 grader 中的其他函数;
- 必须正常从
playGame返回。
评测方式
对每个测试,评测程序会:
- 启动 1 个进程调用
giveHint; - 再启动
N个不同进程,分别调用每位玩家对应的playGame。
所有玩家进程是并行执行的。
评测时,程序总耗时与总内存消耗,按所有这些进程的资源使用总和计算。
你的程序必须:
- 包含头文件
cardgame.h; - 不得实现
main函数; - 不得使用标准输入输出进行交互。
评分与违规情况
设当前子任务的提示范围上界为 ,最大允许轮数为 。
若某个测试中发生以下任一情况,则该测试所在子任务得 0 分:
- 你返回的提示不在区间 内;
- 游戏没有在 轮之内结束;
- 卡牌放置顺序不是非递减的;
- 在调用
placeCard之后仍继续调用 grader 函数; playGame在未调用placeCard的情况下结束。
对于采用 Binary 评分的子任务,只要不违规并且成功完成游戏,即获得该子任务满分。
对于采用 Partial 评分的子任务,若不违规,则会根据所需轮数获得部分分。原题面使用表格给出各子任务的评分类型,详见下方子任务表。
子任务
| 子任务 | 分值 | 评分方式 | 额外限制 | ||||
|---|---|---|---|---|---|---|---|
| 1 | 9 | Binary | 无 | ||||
| 2 | 10 | 所有抽到的值互不相同 | |||||
| 3 | 11 | 无 | |||||
| 4 | 12 | Partial | 至少有一位玩家抽到 | ||||
| 5 | 13 | 所有抽到的值互不相同 | |||||
| 6 | 16 | 无 | |||||
| 7 | 29 |
本地测试(单进程)
题目提供本地 grader:Lgrader.cpp。它可以用来测试:
- 生成提示
giveHint; - 从某个玩家视角模拟
playGame。
测试 giveHint
输入格式:
0N M H
其中 表示第 i 位玩家抽到的卡牌值。
grader 会输出你生成的提示。
测试某位玩家的 playGame
输入格式:
1N M Hi hL
其中:
i:要模拟的玩家编号;h:提示值;L:在该玩家第一轮行动前,已经放下卡牌的玩家数;P:这些玩家的编号列表。
在本地模拟中:
- 若玩家选择
passTurn(),grader 会输出0; - 若玩家选择
placeCard(),grader 会输出1并结束。
每次 grader 输出 0 后,还会再读入:
K
其中 K 表示在这一轮到下一次该玩家行动之间,有多少位玩家放下了卡牌, 为这些玩家的编号列表。
本地测试(完整游戏)
题目还提供了程序 Lmanager.py,用于在本地模拟整场游戏。
你应先将自己的程序与 Lgrader.cpp 一起编译成可执行文件,然后运行:
python Lmanager.py <你的可执行文件路径>
输入格式为:
N M H Rmax
管理器会启动 个进程来模拟整局游戏。
示例交互
设:
且玩家抽到的卡牌分别为:
4 0 0 3
将各进程分别称为“Klimi”、“玩家 0”、“玩家 1”、“玩家 2”、“玩家 3”。
则一组可能的交互过程如下:
- Klimi:调用
giveHint(4, 5, 6, {4, 0, 0, 3}) - Klimi:
giveHint返回1 - 玩家 0:调用
playGame(4, 5, 6, 0, {-1, 0, 0, 3}, 1, {}) - 玩家 0:调用
passTurn() - 玩家 1:调用
playGame(4, 5, 6, 1, {4, -1, 0, 3}, 1, {}) - 玩家 1:调用
placeCard()并结束执行 - 玩家 2:调用
playGame(4, 5, 6, 2, {4, 0, -1, 3}, 1, {1}) - 玩家 2:调用
passTurn() - 玩家 3:调用
playGame(4, 5, 6, 3, {4, 0, 0, -1}, 1, {1}) - 玩家 3:调用
passTurn() - 玩家 0:
passTurn()返回{1} - 玩家 0:再次调用
passTurn() - 玩家 2:
passTurn()返回{} - 玩家 2:调用
placeCard()并结束执行 - 玩家 3:
passTurn()返回{2} - 玩家 3:调用
placeCard()并结束执行 - 玩家 0:
passTurn()返回{2, 3} - 玩家 0:调用
placeCard()并结束执行
最终整局游戏在 3 轮 内成功完成。