题解:P11429 [COCI 2024/2025 #2] 谬误 / Paradoks

· · 题解

题目简述

有五个人 \text{Igor, Lea, Marino, Sonja, Viktor} 玩一个游戏。

游戏共有 n 场。出牌顺序为刚才提到的顺序,第一场的第一个出牌的人是 \text{Sonja},之后每场第一个出牌的人都是上一场的胜者,每人每场只出一张牌。

每个人可以出 \texttt{C,P,Y,Z}(红色,蓝色,黄色,绿色)中的一个颜色和 1 \sim 9 其中一个数字的组合的牌,要求之前打出过的组合不能再次被打出。

每场第一个出牌的人出的牌的颜色称为场风。在这一场中,后面的人都应该出与场风相同颜色的牌,如果没有打出,则在之后的每一场中,他都不能出该场场风颜色的牌。

确认每场胜者方法:

如果有人打出了在之前已经打出过的组合,或者打出了他已经不能出的颜色,则被称为谬误。如果有人出现了谬误,则他在本场的这一场牌无效,也不参与胜者计算。保证每场第一个出牌的人不会谬误。

要求输出谬误出现的次数以及每个谬误是在哪一场出现的和发生谬误人的名字。

思路

不难看出这一题就是一道小/中模拟,并没有什么难的算法,但模拟主要的难点就在于细节很多且出现错误很难找,因为思路都是互相连起来的。

主要模拟方法:双重循环,在第二层循环每读入一张牌操作一次,主要操作如下:

  1. 设置场风牌(第一个出牌的人)
  2. 判断谬误
  3. 判断是否出牌颜色为场风牌
  4. 更新赢家
  5. 记录卡片

最后在第二层循环结束后,更新上一场赢家,为下一场做准备。

AC Code

#include<map>
#include<set>
#include<list>
#include<stack>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;

#define endl '\n'
typedef long long ll;
typedef unsigned int ui;
typedef pair<ll,ll> pll;
typedef pair<int,int> pii;
const double PI = acos(-1.0);
typedef unsigned long long ull;
// ----------------------------                     结构体 
struct paradox {
    int session;
    string name;
};                                               // 谬误存储结构体 
// ----------------------------                     变量 
string names[10];                                // 编号->名字
vector<paradox> paradoxes;                       // 谬误存储 
unordered_map<char,bool> ban_colors[10];         // 每个人的不可使用颜色 
unordered_map<string,bool> appeared_cards;       // 已出现的卡片 
// ----------------------------                     函数 

int main() {
    int n;
    cin>>n;
    // ------------------------
    names[1] = "IGOR";
    names[2] = "LEA";
    names[3] = "MARINO";
    names[4] = "SONJA";
    names[5] = "VIKTOR";
    string card;                                 // 输入卡片 
    char now_main_color;                         // 当前场风 
    int last_winner_idx = 4;                     // 上一场的赢家 
    for (int i=1;i<=n;i++) {
        bool flag = true;
        int max_point = 0;                       // 当前符合条件卡片最大点数 
        int now_winner_idx = 0;                  // 当前最大卡片人的编号 
        bool is_have_red = false;                // 当场是否出现了红牌 
        for (int j=last_winner_idx;j!=last_winner_idx || flag;j=j%5+1) {
            flag = false;
            cin>>card;
            if (j == last_winner_idx) now_main_color = card[0];     // 如果是第一位出牌的玩家,将场风设定为他的牌的颜色 
            if (appeared_cards[card] || ban_colors[j][card[0]]) {   // 判断是否出现谬误 
                paradoxes.push_back({i,names[j]});                  // 存储进谬误行列 
                continue;
            }
            if (card[0] != now_main_color) ban_colors[j][now_main_color] = true;         // 如果没有出场风颜色,则这个人以后都不能出这个颜色 
            // 更新赢家
            if (!is_have_red) {                  // 如果在这之前还没有出现红牌 
                if (card[0]=='C' || card[0]==now_main_color && card[1]-'0'>max_point) {  // 如果这张卡片是红色的 或者 这张卡片的颜色和场风颜色相同 且 点数大于当前最大点数 
                    now_winner_idx = j;
                    max_point = card[1] - '0';
                }
                if (card[0]=='C') is_have_red = true;               // 如果这张卡片是红色的,那么现在就已经出现了红牌 
            }
            else {                               // 已经出现了红牌 
                if (card[0]=='C' && card[1]-'0'>max_point) {        // 如果这张卡片也是红牌且点数大于当前最大点数 
                    now_winner_idx = j;
                    max_point = card[1] - '0';
                }
            }
            appeared_cards[card] = true;         // 记录卡片 
        }
        last_winner_idx = now_winner_idx;        // 将上一场赢家设为这一场赢家,为下一场做准备 
    }
    // ------------------------
    cout<<paradoxes.size()<<endl;
    for (paradox i:paradoxes) cout<<i.session<<' '<<i.name<<endl;
    return 0;
}