题解:洛谷 P9958 Shell Game
【题目来源】
洛谷:P9958 [USACO19JAN] Shell Game B - 洛谷
【题目描述】
为了消磨时光,奶牛Bessie和她的朋友Elsie喜欢玩一种她们在农业展览会上看到的游戏。
游戏准备阶段,Bessie在桌子上放置三个倒置的坚果壳,并在其中一个坚果壳下面藏了一块小的鹅卵石(至少她希望这是一块鹅卵石——她在一块牧场的地上找到的)。随后Bessie会两两调换坚果壳,同时Elsie试着去猜鹅卵石的位置。
奶牛们在农业展览会上看到的这个游戏的标准形式是玩家可以看到鹅卵石初始的位置,然后要求玩家猜所有交换完成之后鹅卵石最终的位置。
然而,现在奶牛们想要去进行这样一种玩法,Elsie不知道鹅卵石的初始位置,同时她可以在每一次交换之后猜一下鹅卵石的位置。Bessie知道正确答案,在游戏结束后会给Elsie一个分数,等于她猜对的次数。
给定所有的交换和Elsie的猜测,但是不给出鹅卵石的初始位置,请求出Elsie最高可能获得的分数。
【输入】
输入的第一行包含一个整数\(N\),为交换的次数\((1\le N\le 100)\)。以下\(N\)行每行描述了游戏的一个回合,包含三个整数\(a\)、\(b\)和\(g\),表示Bessie交换了坚果壳\(a\)和\(b\),然后Elsie猜的是坚果壳\(g\)。所有这三个数均为\(1\)、\(2\)、\(3\)之一,并且\(a\neq b\)。
【输出】
输出Elsie可以得到的最高分数。
【输入样例】
3
1 2 1
3 2 1
1 3 1
【输出样例】
2
【核心思想】
-
问题分析:游戏有 3 个位置(1、2、3),鹅卵石初始位置未知(可能为 1、2 或 3)。共进行 \(N\) 次操作,每次操作包含:先交换位置 \(a\) 和 \(b\) 上的坚果壳,然后 Elsie 猜测鹅卵石在位置 \(g\)。若猜中则得 1 分。求 Elsie 在最优初始位置选择下能获得的最高总分。由于初始位置只有 3 种可能,直接枚举所有情况,模拟交换过程并统计得分,取最大值即可。
-
算法选择:
- 枚举 + 模拟:分别假设鹅卵石初始在 1 号、2 号、3 号位置,各自独立模拟 \(N\) 次交换和猜测过程,记录得分,取最高分。
-
关键步骤:
- 读入数据:读取 \(N\) 和 \(N\) 组三元组 \((a, b, g)\)。
- 枚举三种初始位置:
- 用数组
pos[4]表示 3 个位置是否有鹅卵石(例如初始在位置 1 时,pos[1]=1, pos[2]=0, pos[3]=0)。 - 对每次操作:
- 交换位置
a和b上的值:swap(pos[a], pos[b])。 - 检查猜测位置
g:若pos[g] == 1,则当前模拟得分加 1。
- 交换位置
- 记录该初始位置下的总得分。
- 用数组
- 取最大值:3 次模拟后,输出最高得分。
-
时间/空间复杂度:
- 时间复杂度:\(O(3 \cdot N) = O(N)\),\(N \le 100\)。
- 空间复杂度:\(O(1)\)。
-
枚举所有可能性的核心思想:
- 未知初始状态:由于鹅卵石初始位置未知,且只有 3 种可能,直接枚举所有可能状态是简单且正确的做法。
- 操作一致性:交换操作与猜测分数计算完全由位置决定,与初始状态无关,因此可独立模拟每种情况。
- 取最优:Elsie 无法控制初始位置,但问题求“最高可能分数”,等价于从所有可能的初始位置中选择最优的一个。
- 适用场景:适用于状态空间极小(≤ 3)、且操作规则不依赖历史、只与当前位置有关的模拟问题。
【解题思路】

【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n;
struct node {
int a, b, g;
}p[105];
// ifstream filein("shell.in");
// ofstream fileout("shell.out");
int main()
{
cin >> n;
// filein >> n; // 输入n
for (int i=1; i<=n; i++) { // 依次输入n行交换的位置和猜的位置
cin >> p[i].a >> p[i].b >> p[i].g;
}
int maxn = 0, tot=0; // 定义最高分数,初始为0。定义第一轮统计分数,初始为0
int a[4] = {0,1,0,0}; // 如果鹅卵石在第1个位置
for (int i=1; i<=n; i++) { // 按照n行记录进行位置交换
swap(a[p[i].a], a[p[i].b]);
if (a[p[i].g] == 1) tot++; // 每次交换后检查猜的位置是否有鹅卵石,如果有的话统计分数自增1
}
maxn = max(maxn, tot); // 比较得到最高分数
tot=0; // 第二轮统计分数,重新初始化为0
int b[4] = {0,0,1,0}; // 如果鹅卵石在第2个位置
for (int i=1; i<=n; i++) { // 按照n行记录进行位置交换
swap(b[p[i].a], b[p[i].b]);
if (b[p[i].g] == 1) tot++; // 每次交换后检查猜的位置是否有鹅卵石,如果有的话统计分数自增1
}
maxn = max(maxn, tot); // 比较得到最高分数
tot=0; // 第三轮统计分数,重新初始化为0
int c[4] = {0,0,0,1}; // 如果鹅卵石在第3个位置
for (int i=1; i<=n; i++) { // 按照n行记录进行位置交换
swap(c[p[i].a], c[p[i].b]);
if (c[p[i].g] == 1) tot++; // 每次交换后检查猜的位置是否有鹅卵石,如果有的话统计分数自增1
}
maxn = max(maxn, tot); // 比较得到最高分数
cout << maxn << endl; // 最后输出最高分数
return 0;
}
【运行结果】
3
1 2 1
3 2 1
1 3 1
2
浙公网安备 33010602011771号