模拟训练题

Preface

这里是训练题的一些题解,本次题解主要是模拟相关,模拟的题目大多考验码力,多做模拟可以训练代码修补以及逻辑判断能力。

A - Suffix Three

原题链接

题目大意

说是以po结尾的是他加禄语(菲律宾语),以dese/masu结尾(です/ます)的是日语,以mnida结尾的是韩语。

然后判断下面的句子分别是哪种语言。

后面几个句子除了后缀都不是对应语言啊喂

思路

\(java\)有现成的endsWith方法判断字符串末尾,所以直接用就行。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        try (Scanner s = new Scanner(System.in)) {
            int t = s.nextInt();
            for (int i = 0; i < t; i++) {
                String s1 = s.next();
                if (s1.endsWith("po")) {
                    System.out.println("FILIPINO");
                } else if (s1.endsWith("desu") || s1.endsWith("masu")) {
                    System.out.println("JAPANESE");
                } else {
                    System.out.println("KOREAN");
                }
            }
        }
    }
}

B - Dreamoon and Ranking Collection

原题链接

题目大意

巨佬Dreamoon天天打CF,给你几场比赛rank,问你再打\(k\)场,最多能集齐\(1\)\(n\)的几场。

思路

我们考虑建立一个数组存储每个rank数是否用过,这里建立为局部变量防止每次用的时候需要初始化。

注意数组需要开大一些,不止题目的100,至少需要200以上, 有同学在这里WA了很多次。

\(sort\)后依次遍历即可。

#include <iostream>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
typedef long long LL;
using namespace std;

void solve() {
    bool a[500] = {false};
    int n, x;
    cin >> n >> x;
    for (int i = 0; i < n; i++) {
        int t;
        cin >> t;
        a[t] = true;
    }
    for (int i = 1; i < 500; i++) {
        if (!a[i]) {
            if (x) {
                a[i] = true;
                x--;
            } else {
                cout << i - 1 << endl;
                break;
            }
        }
    }
}

int main() {
    ios;
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

C - Symmetric Matrix

原题链接

题目大意

给定\(n\)\(2\times2\)的矩阵以及边长\(m\),问能否拼成一个沿对角线对称且边长为\(m\)的地板。

思路

问的是能否,并且不限制每种瓷砖的数量。所以我们只需要一块能够对角线对称的瓷砖然后全局填充即可。而当\(m\)为奇数的时候是不可能拼成地板的,所以稍微判断一下这两种情况就行了。

对角线对称即左下与右上相等,读入时判断即可。

#include <iostream>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
typedef long long LL;
using namespace std;

void solve() {
    int n, m;
    cin >> n >> m;
    bool flag = false;
    for (int i = 1; i <= n; i++) {
        int a, b, c, d;
        cin >> a >> b;
        cin >> c >> d;
        if (b == c) {
            flag = true;
        }
    }
    if (m & 1) {
        flag = false;
    }
    puts(flag ? "YES" : "NO");
}

int main() {
    ios;
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

D - Happy Birthday, Polycarp!

原题链接

题目大意

有个小崽子要过生日,他认为只有重复的某一个数字的数是好数,于是问你从\(1\)\(n\)有几个好数。

思路

这题很明显,每位数都有\(9\)个好数,根据输入的\(n\)来判断位数即可,但最高位要另外处理。所以我直接遍历\(1\)\(9\)计入每个数字重复多少次才不超过n,加在一起即可。

#include <iostream>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
typedef long long LL;
using namespace std;

void solve() {
    int n;
    cin >> n;
    int ans = 0;
    for (int i = 1; i <= 9; i++) {
        long long num = i;
        while (num <= n) {
            ans++;
            num = num * 10 + i;
        }
    }
    cout << ans << endl;
}

int main() {
    ios;
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

E - A New Technique

原题链接

题目大意

鳴人くん 又在训练了,他想要获得新技术。题目给出了行数和列数,以及每行每列的值,现在让我们还原这个矩阵。

思路

我们只需要根据题目给出的列得到行的顺序,然后按照这个顺序排行就行。

考虑到map的自动排序,我们可以把数据和顺序使用map<int, int>存储。

#include <iostream>
#include <map>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
typedef long long LL;
using namespace std;

const int N = 510;

map<int, int> mp;
int a[N][N];
int news[N];

void solve() {
    int n, m;
    mp.clear();
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int tmp;
        cin >> tmp;
        mp[tmp] = i;
        for (int j = 1; j <= m; j++) {
            if (j == 1) {
                a[i][j] = tmp;
            } else {
                cin >> a[i][j];
            }
        }
    }
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            int tmp;
            cin >> tmp;
            if (mp[tmp] != 0) {
                news[j] = tmp;
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cout << a[mp[news[i]]][j] << " \n"[j == m];
        }
    }
}

int main() {
    ios;
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

F - Reachable Numbers

原题链接

题目大意

定义一个函数,使得\(f(x) = x + 1\)去掉后缀\(0\),而\(Reachable\)的定义是迭代函数\(f(x)\)能出现的不同数的个数。

思路

这题很直接,我们直接实现这样一个函数,并且开一个数组去存放我们迭代过程中遇到的所有数,当迭代过程中遇到一个之前迭代过的数则跳出循环(否则会出死循环)。

这题不需要设置\(t\)循环别忘了

#include <iostream>
#include <map>
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
typedef long long LL;
using namespace std;

map<int, bool> mp;

void solve() {
    int n, res = 0;
    mp.clear();
    cin >> n;
    while (!mp[n]) {
        mp[n] = true;
        n++;
        res++;
        while (!(n % 10)) {
            n /= 10;
        }
    }
    cout << res;
}

int main() {
    ios;
//    int t;
//    cin >> t;
//    while (t--) {
        solve();
//    }
    return 0;
}

G - Collecting Packages

原题链接

题目大意

笨蛋程序员的代码跑不了,所以他跑了,需要我们把他的机器人设定成能收集所有包裹的机器人。但那家伙的机器人只能向右或者向上走,分别用RU表示。当能完成任务的时候输出YES以及路径指令,否则输出NO

思路

我们把输入的点按照\(x\)\(y\)坐标排序,排序后所有点都满足:

\[\left\{ \begin{aligned} x_i \geq x_{i-1} \\ y_i \geq y_{i-1} \end{aligned} \right. \]

则可以达到,按照每个\(x\)\(y\)输出R U即可,否则输出NO

#include <iostream>
#include <map>
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
typedef long long LL;
using namespace std;

struct Point{
    int x;
    int y;
}point[1010];

bool cmp(Point a, Point b) {
    return a.x != b.x ? a.x < b.x : a.y < b.y;
}

bool upper(Point a, Point b) {
    return a.y > b.y;
}

void solve() {
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> point[i].x >> point[i].y;
    }
    sort(point, point + n, cmp);
    for (int i = 1; i < n; ++i) {
        if (upper(point[i - 1], point[i])) {
            cout << "NO" << endl;
            return;
        }
    }
    cout << "YES" << endl;
    int xIMa = 0, yIMa = 0; // IMa是いま的意思 
    for (int i = 0; i < n; ++i) {
        while (xIMa++ < point[i].x) {
            cout << "R";
        }
        while (yIMa++ < point[i].y) {
            cout << "U";
        }
        xIMa--;
        yIMa--;
    }
    cout << endl;
}

int main() {
    ios;
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

H - Yet Another Crosses Problem

原题链接

题目大意

给你若干\(n\)\(m\)列的矩阵,其中有些格子是黑(*)的,问你至少再涂几个格子能得到一个十字架。

思路

直接贪! 我们找到白的最少的那一行和列,特判一下交界是不是黑色即可。

#include <iostream>
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
typedef long long LL;
using namespace std;

const int N = 5e4 + 10;

string s[N];
int a[N], b[N];

void solve() {
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < max(n, m); i++) {
        a[i] = 0;
        b[i] = 0;
    }
    int res = 1e9;
    for (int i = 0; i < n; i++) {
        cin >> s[i];
        for (int j = 0; j < m; j++) {
            if (s[i][j] == '.') {
                a[i]++;
                b[j]++;
            }
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            res = min(res, s[i][j] == '*' ? a[i] + b[j] : a[i] + b[j] - 1);
        }
    }
    cout << res << endl;
}

int main() {
    ios;
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

I - RGB Substring (easy version)

原题链接

题目大意

看到easy就知道是C1/D1呢

很多情况下easy version都是让我们暴力模拟的,那么这题肯定也可以。

问我们至少要改几次使得长度为\(n\)的字符串的长度为\(k\)的子串成为RGB循环串的字串。

思路

既然都暴力了就没有什么好说的了,每一串每一串的算,找到最小值输出就行了。

#include <iostream>
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
#define endl '\n'
typedef long long LL;
using namespace std;

void solve() {
    int n, k;
    string s;
    cin >> n >> k >> s;
    int min_ = 1e9;
    for (int i = 0; i <= n - k; i++) {
        string ss = s.substr(i, k);
        string rets = "RGB";
        for (int j = 0; j < 3; j++) {
            int l = j, cnt = 0;
            string str;
            while (cnt < k) {
                str += rets[l];
                l++;
                if (l == 3) {
                    l = 0;
                }
                cnt++;
            }
            int x = 0;
            for (int m = 0; m < k; m++) {
                if (ss[m] != str[m]) {
                    x++;
                }
            }
            min_ = min(x, min_);
        }
    }
    cout << min_ << endl;
}

int main() {
    ios;
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

J - System Testing

原题链接

题目大意

我们要计算cf的系统测试环节耗时,有\(k\)台评测机在干活,总测试数为\(n\)时,当前测试完成\(m\)个测试代码,进度\(d\)计算公式为:

\[d = round(100 \times \frac{m}{n}), round(x) = \lfloor x + 0.5\rfloor \]

当当前测试的测试点序号与测试进度相等时,表示一个有趣的提交,让我们计算一共有几个有趣的提交。

思路

难度有些大,参考了一篇题解的做法。

用大根堆维护每个题目测试的开始与结束时间,计算每个题目的运行区间以及每个时刻的进度,当进度在区间内可以判断存在有趣提交。

附上原题解链接: 点这里!

#include <iostream>
#include <queue>
#include <cmath>
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
#define endl '\n'
typedef long long LL;
using namespace std;

const int N = 1010;

int a[N];

// 这题真不会了,看了几遍题解照着写的。

struct Problem {
    int id;
    int num;
    bool operator< (const Problem &t) const {
        return num > t.num;
    }
};

struct FinPro {
    int left;
    int right;
}finPro[N];

int pre[150 * N];
int round_[150 * N];

struct Process{
    int left;
    int right;
    int num;
}process[N];

priority_queue<Problem> heap;

void solve() {
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; ++i) {
        if (heap.size() < k) {
            heap.push(Problem{i, a[i]});
        } else {
            Problem cur = heap.top();
            finPro[cur.id].left = cur.num - a[cur.id];
            finPro[cur.id].right = cur.num;
            heap.pop();
            heap.push(Problem{i, a[i] + cur.num});
        }
    }
    int last = 0;
    while (!heap.empty()) {
        Problem cur = heap.top();
        finPro[cur.id].left = cur.num - a[cur.id];
        finPro[cur.id].right = cur.num;
        last = max(last, cur.num);
        heap.pop();
    }
    for (int i = 1; i <= n; ++i) {
        pre[finPro[i].right]++;
    }
    for (int i = 1; i <= last; ++i) {
        pre[i] += pre[i - 1];
    }
    for (int i = 1; i <= last; ++i) {
        round_[i] = floor(1.0 * pre[i] / n * 100 + 0.5);
    }
    int cnt = 0;
    for (int i = 1; i <= last; ++i) {
        if (round_[i] != round_[i - 1]) {
            process[++cnt].num = round_[i];
            process[cnt].left = i + 1;
            if (cnt) {
                process[cnt - 1].right = i;
            }
        }
    }
    process[cnt].right = last + 1;
    int res = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= cnt; ++j) {
            if (process[j].left <= finPro[i].right && process[j].right >= finPro[i].left &&
                process[j].left <= finPro[i].left + process[j].num &&
                min(finPro[i].right, process[j].right) >= finPro[i].left + process[j].num) {
                res++;
                break;
            }
        }
    }
    cout << res << endl;
}

int main() {
    ios;
//    int t;
//    cin >> t;
//    while (t--) {
        solve();
//    }
    return 0;
}

K - Busy Robot

原题链接

题目大意

你有一个机器人,你需要给机器人下指令,让他去到一个地方,但是他在前进的时候不会听你的。现在让我们检测多少条指令是成功执行的。

思路

注意题目说了被忽略的指令也可以是成功的,我们只需要模拟出机器人的行进过程,同时使用结构体存储每一条指令,和目的地判断即可。

有被1800晃到

#include <iostream>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;

const int N = 1e5 + 10;

struct Command{
    LL time;
    LL op;
}com[N];

inline void solve() {
    LL n;
    cin >> n;
    LL ans = 0;
    for (LL i = 1; i <= n; ++i) {
        cin >> com[i].time >> com[i].op;
    }
    com[n + 1].time = 2e18;
    LL timeN = 1, lastT = 1, pos = 0, lastOP = 0;
    for (LL i = 1; i <= n; ++i) {
        if (com[i].time < timeN) {
            LL dir;
            if (pos < lastOP) {
                dir = -1;
            } else if (pos == lastOP) {
                dir = 0;
            } else {
                dir = 1;
            }
            LL nowOP = lastOP + dir * (com[i].time - lastT);
            LL nowOPr = lastOP + dir * ((min(timeN, com[i + 1].time)) - lastT);
            if ((nowOP <= com[i].op && com[i].op <= nowOPr) || (nowOPr <= com[i].op && com[i].op <= nowOP)) {
                ++ans;
            }
        } else {
            lastT = max(timeN, com[i].time);
            timeN = com[i].time + abs(com[i].op - pos);
            lastOP = pos;
            pos = com[i].op;
            if (timeN <= com[i + 1].time) {
                ++ans;
            }
        }
    }
    cout << ans << endl;
}

int main() {
    ios;
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

L - 神奇的幻方

原题链接

题目大意

有个好玩的方阵叫做幻方,就是小时候玩的那个行列对角和相等的玩意。

思路

既然给了构造方法,模拟构造一下就行。

#include <iostream>
using namespace std;

int n, a[40][40], x, y;

int main(){
    cin >> n;
    x = 1;
    y = (n+1) / 2;
    for(int i = 1; i <= n*n; i++){
        a[x][y] = i;
        if(!a[(x-2+n) % n + 1][y % n + 1]) {
            x = (x-2+n) % n + 1;
            y = y % n + 1;
        } else {
            x = x % n + 1;
        }
    }
    for(int i = 1; i <= n;i++) {
        for(int j = 1; j <= n; j++) {
            cout << a[i][j] << ' ';
        }
        cout << endl;
    }
}

M - 时间复杂度

原题链接

题目大意

A++语言的循环和C++以及Java的很像,问你小明计算的时间复杂度\(O(f(x))\)是否正确,以及代码是否正确。

思路

首先我们要知道ERR的情况,FE不匹配,这个可以通过栈很轻松的解决(但是我模拟了5次之后搞不出来于是手写了个差不多的东西)。其次就是判断复杂度,读入特判\(O(1)\)后利用字符串转整形的操作获取小明的复杂度。另外,要考虑循环不执行的情况,当初始值是\(n\)但是结束条件不是\(n\)时以及两者都是常数但是前者大于后者时,该循环以及其嵌套的所有内层循环全部失效,计算复杂度的时候可以后期--回来。(即便如此我还是重写了8个版本)

下面这个代码就别看了,我都不知道我咋写的

#include <iostream>
#include <cstring>
#include <stack>
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
#define endl '\n'
typedef long long LL;
using namespace std;

int t, l, z, m, f, ans, num1, sum1, sum2, cnt;

char ch, ch_[300];

bool flag, vis[1500];

string s1, s2, s3;

inline void init() {
    flag = true;
    num1 = z = m = ans = f = sum1 = sum2 = cnt = 0;
    memset(vis, 0, sizeof(vis));
}

inline void judge() {
    if (l & 1) {
        flag = false;
    }
    if (s1.size() == 4) {
        num1 = 0;
    } else {
        for (int i = 4; i < s1.size() - 1; i++) {
            num1 = num1 * 10 + (s1[i] - '0');
        }
    }
    for (int p = 1; p <= l; p++) {
        cin >> ch;
        if (ch == 'F') {
            sum1++;
            cin >> ch_[++cnt];
            if (vis[ch_[cnt] - 'a' + 1] == 1) {
                flag = false;
            }
            vis[ch_[cnt] - 'a' + 1] = true;
            cin >> s2 >> s3;
            if (!flag) {
                continue;
            }
            if (f) {
                f++;
                continue;
            }
            if (s2[0] == 'n' && s3[0] != 'n') {
                f++;
                continue;
            }
            if (s2[0] != 'n' && s3[0] == 'n') {
                z++;
            }
            if (s2[0] != 'n' && s3[0] != 'n') {
                if (s2.size() > s3.size()) {
                    f++;
                    continue;
                }
                m++;
                if (s2.size() == s3.size()) {
                    for (int i = 0; i < s2.size(); i++) {
                        if (s2[i] > s3[i]) {
                            m--;
                            f++;
                            continue;
                        }
                        if (s2[i] < s3[i]) {
                            break;
                        }
                    }
                }
            }
        }
        if (ch == 'E') {
            if (!flag) {
                continue;
            }
            sum2++;
            vis[ch_[cnt--] - 'a' + 1] = false;
            if (f) {
                f--;
                continue;
            }
            if (z > 0) {
                ans = max(ans, z);
                !m ? z-- : m--;
            }
        }
    }
}

inline void print() {
    printf(!flag || sum1 != sum2 || f ? "ERR" : ans == num1 ? "Yes" : "No");
    printf("\n");
}

int main() {
    scanf("%d", &t);
    for (int k = 1; k <= t; k++) {
        init();
        scanf("%d", &l);
        cin >> s1;
        judge();
        print();
    }
    return 0;
}

N - 逻辑表达式

原题链接

题目大意

和咱们平常见到的计算式子本质上没有什么不同,现在让我们计算式子的值,并且要算出与和或分别短路了几次。

思路

用栈构造后缀表达式。

构建一个符号栈以及一个布尔值栈,我们先让原字符串左右加上一对括号,方便后续计算。

我们从头扫串扫到尾,遇到左括号在符号栈压入底数,表示括号的开始。遇到布尔值直接压入等待计算。遇到右括号则直接计算括号内所有表达式的运算值及短路并弹出左括号压栈。遇到逻辑运算符直接运算,并且将运算结果压入布尔值栈。当运算完后还有运算符时,处理短路stkOp.top() == stkN.top()是一个巧妙的做法,因为设定的短路条件和压入的运算符栈表值恰好相同。cnt计数是为了方便短路后取消内部短路而设置的。和上面时间复杂度中的嵌套循环异常处理类似。

#include <iostream>
#include <string>
#include <stack>
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;

stack<int> stkOp;
stack<int> stkN;

int main() {
    ios;
    string s;
    cin >> s;
    s = "(" + s + ")";
    unsigned long long len = s.length();
    string now;
    int cnt = 0;
    int andSc = 0, orSc = 0;
    for (int i = 0; i < len; ++i) {
        now = s.substr(i, 1);
        if (now == "0" || now == "1") {
            stkN.push(now == "0" ? 0 : 1);
        }
        if (now == "(") {
            stkOp.push(-1);
        }
        if (now == ")") {
            int optNow;
            while (stkOp.top() >= 0) {
                optNow = stkOp.top();
                stkOp.pop();
                int tmp1, tmp2;
                tmp2 = stkN.top();
                stkN.pop();
                tmp1 = stkN.top();
                stkN.pop();
                if (optNow == tmp1) {
                    --cnt;
                }
                stkN.push(optNow == 0 ? tmp1 & tmp2 : tmp1 | tmp2);
            }
            stkOp.pop();
        }
        if (now == "&" || now == "|") {
            while (!stkOp.empty()) {
                if ((stkOp.top() == 1 && now == "&") || stkOp.top() < 0) break;
                int optNow = stkOp.top();
                stkOp.pop();
                int tmp1 = stkN.top();
                stkN.pop();
                int tmp2 = stkN.top();
                stkN.pop();
                if (tmp2 == optNow) {
                    --cnt;
                }
                stkN.push(optNow == 0 ? tmp1 & tmp2 : tmp1 | tmp2);
            }
            stkOp.push(now == "|" ? 1 : 0);
        }
        if (now == "&" || now == "|") {
            if (stkOp.top() == stkN.top()) {
                if (!cnt) {
                    (stkOp.top() ? orSc : andSc)++;
                }
                ++cnt;
            }
        }
    }
    cout << stkN.top() << endl;
    cout << andSc << " " << orSc << endl;
    return 0;
}

O - 猪国杀

原题链接

思路

大模拟!!!贴心且具有代码硬实力的5eqn同学帮我们AC了这道题,下面是他对这道题一些要点的讨论:

使用递归处理无懈可击,决斗可能会把自己打死,如果决斗的时候自己死了,要停止发牌,否则秽土转生。并且反猪决斗的时候优先找主猪。

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>

#define ll long long
#define fori(u) for (ll i = 0; i < (u); i++)
#define forj(u) for (ll j = 0; j < (u); j++)

using namespace std;

typedef enum {
    MASTER,
    ZHONG,
    FAN,
} Team;

typedef enum {
    PEACH,
    KILL,
    DODGE,
    FIGHT,
    NANZHU,
    WANJIAN,
    JI,
    ZHUGE,
} Card;

typedef enum {
    EXACT_MASTER,
    EXACT_ZHONG,
    EXACT_FAN,
    UNKNOWN,
    LIKEFAN,
} Intel;

typedef enum {
    BAD,
    GOOD,
    NONE,
} Intent;

typedef struct pig {
    int team;
    int health;
    bool equipped;
    int counts[8];

    int count;
    int cards[2048];
} Pig;

Pig pigs[16];
int intel[16];
bool died[16];
int pile[2048];
int *pilePtrPtr;
int m;
bool ended = false;
bool masterWin = false;

void use(int currentPig, int card) {
    pigs[currentPig].counts[card]--;
    int count = pigs[currentPig].count;
    fori(count) {
        if (pigs[currentPig].cards[i] == card) {
            memcpy(pigs[currentPig].cards + i, pigs[currentPig].cards + i + 1, sizeof(int) * (count - i - 1));
            break;
        }
    }
    pigs[currentPig].count--;
}

void append(int currentPig, int card) {
    pigs[currentPig].counts[card]++;
    pigs[currentPig].cards[pigs[currentPig].count] = card;
    pigs[currentPig].count++;
}

char fromIndex(int index) {
    switch (index) {
        case 0:
            return 'P';

        case 1:
            return 'K';

        case 2:
            return 'D';

        case 3:
            return 'F';

        case 4:
            return 'N';

        case 5:
            return 'W';

        case 6:
            return 'J';

        case 7:
            return 'Z';
    }

    return -1;
}

int fromName(char name) {
    switch (name) {
        case 'P':
            return 0;

        case 'K':
            return 1;

        case 'D':
            return 2;

        case 'F':
            return 3;

        case 'N':
            return 4;

        case 'W':
            return 5;

        case 'J':
            return 6;

        case 'Z':
            return 7;
    }

    return -1;
}

int fromTeam(const string& team) {
    if (team == "MP") {
        return 0;
    } else if (team == "ZP") {
        return 1;
    } else if (team == "FP") {
        return 2;
    }

    return -1;
}

bool canReach(int from, int to, int n) {
    int count = 0;
    int ptr = from;

    while (true) {
        if (ptr == n - 1) {
            ptr = 0;
        } else {
            ptr++;
        }

        if (!died[ptr]) {
            count++;
        }

        if (ptr == to) {
            return count <= 1;
        }
    }
}

void printPig(Pig *pig, int index) {
    cout << "[PIG] " << index << (died[index] ? "x" : (pig->equipped ? "+" : "")) << " (" << pig->team <<
         ") (HP = " << pig->health << ") has [";
    fori(7) {
        cout << pig->counts[i] << ", ";
    }
    cout << pig->counts[7] << "]" << endl;
    cout << "      Order: ";
    fori(pig->count) {
        cout << fromIndex(pig->cards[i]) << ' ';
    }
    cout << endl;
}

// game logic level abstraction

bool broadcastJi(int currentPig, bool malicious, int n);
bool decideJi(int currentPig, int target, bool malicious, int n);

void distribute(int currentPig, int *pilePtr) {
    fori(2) {
        if (*pilePtr == m) {
            append(currentPig, pile[*pilePtr - 1]);
        } else {
            append(currentPig, pile[*pilePtr]);
            (*pilePtr)++;
        }
    }
}

void distribute3(int currentPig, int *pilePtr) {
    fori(3) {
        if (*pilePtr == m) {
            append(currentPig, pile[*pilePtr - 1]);
        } else {
            append(currentPig, pile[*pilePtr]);
            (*pilePtr)++;
        }
    }
}

// return INTENT
int attemptHarm(int currentPig, int target) {
    if (intel[target] == MASTER || intel[target] == ZHONG) {
        if (intel[currentPig] == UNKNOWN || intel[currentPig] == LIKEFAN) {
            intel[currentPig] = FAN;
        }

        return BAD;
    } else if (intel[target] == FAN) {
        if (intel[currentPig] == UNKNOWN || intel[currentPig] == LIKEFAN) {
            intel[currentPig] = ZHONG;
        }

        return GOOD;
    } else {
        return NONE;
    }
}

void harm(int currentPig, int target, int n) {

    if (pigs[target].health == 1) {
        if (pigs[target].counts[PEACH] == 0) {
            died[target] = true;

            if (pigs[target].team == MASTER) {
                if (!ended) {
                    ended = true;
                    masterWin = false;
                }
            } else if (pigs[target].team == FAN) {
                bool willEnd = true;
                fori(n) {
                    if (!died[i] && pigs[i].team == FAN) {
                        willEnd = false;
                        break;
                    }
                }

                if (willEnd) {
                    if (!ended) {
                        ended = true;
                        masterWin = true;
                    }
                } else {
                    // don't add card if ended
                    distribute3(currentPig, pilePtrPtr);
                }
            } else if (pigs[target].team == ZHONG) {
                if (pigs[currentPig].team == MASTER) {
                    memset(pigs[currentPig].counts, 0, sizeof(int) * 8);
                    memset(pigs[currentPig].cards, 0, sizeof(int) * pigs[currentPig].count);
                    pigs[currentPig].count = 0;
                    pigs[currentPig].equipped = false;
                }
            }
        } else {
            use(target, PEACH);
        }
    } else {
        pigs[target].health--;
    }
}

void harmCanDodge(int currentPig, int target, bool obscure, int dodgeCard, int n) {
    if (!obscure) {
        attemptHarm(currentPig, target);
    }

    if (pigs[target].counts[dodgeCard] == 0) {
        harm(currentPig, target, n);

        if (intel[target] == MASTER) {
            if (intel[currentPig] == UNKNOWN) {
                intel[currentPig] = LIKEFAN;
            }
        }
    } else {
        use(target, dodgeCard);
    }
}

void aoe(int currentPig, int card, int target, int n, int dodgeCard) {
    fori(n - 1) {
        int index = (i + currentPig + 1) % n;

        if (died[index]) {
            continue;
        }

        if (intel[index] == MASTER || intel[index] == ZHONG) {
            if (broadcastJi(currentPig, true, n)) {
                continue;
            }
        }

        if (intel[index] == FAN) {
            if (broadcastJi(currentPig, false, n)) {
                continue;
            }
        }

        harmCanDodge(currentPig, index, true, dodgeCard, n);

        if (ended) {
            return;
        }
    }
}

void fight(int currentPig, int target, int n) {
    int intent = attemptHarm(currentPig, target);

    // block ji on unknown intent action
    if (intent != NONE) {
        // try ji
        if (broadcastJi(currentPig, intent == BAD, n)) {
            return;
        }
    }

    bool isTargetTurn = true;

    while (true) {
        if (isTargetTurn) {
            if (pigs[target].counts[KILL] == 0 || (pigs[target].team == ZHONG && pigs[currentPig].team == MASTER)) {
                harm(currentPig, target, n);
                break;
            } else {
                use(target, KILL);
            }
        } else {
            if (pigs[currentPig].counts[KILL] == 0) {
                harm(target, currentPig, n);
                break;
            } else {
                use(currentPig, KILL);
            }
        }

        // switch place
        isTargetTurn = !isTargetTurn;
    }
}

void use(int currentPig, int card, int target, int n) {
    use(currentPig, card);

    switch (card) {
        case PEACH:
            if (pigs[currentPig].health < 4) {
                pigs[currentPig].health++;
            } else {
                cout << "[ERROR] using peach when saturated" << endl;
            }

            break;

        case KILL:
            harmCanDodge(currentPig, target, false, DODGE, n);
            break;

        case FIGHT:
            fight(currentPig, target, n);
            break;

        case NANZHU:
            aoe(currentPig, card, target, n, KILL);
            break;

        case WANJIAN:
            aoe(currentPig, card, target, n, DODGE);
            break;
    }
}

int next(int currentPig, int n) {
    if (currentPig == n - 1) {
        currentPig = 0;
    } else {
        currentPig++;
    }

    // prevent dead pig
    while (died[currentPig]) {
        if (currentPig == n - 1) {
            currentPig = 0;
        } else {
            currentPig++;
        }
    }

    return currentPig;
}

// decision level abstraction

void decide(int currentPig, int n) {
    bool killUsed = false;
    bool cardUsed;
    bool fightUsed;
    int card;

    while (!died[currentPig]) {
        cardUsed = false;
        fori(pigs[currentPig].count) {
            card = pigs[currentPig].cards[i];

            switch (card) {
                case PEACH:
                    if (pigs[currentPig].health < 4) {
                        use(currentPig, PEACH, currentPig, n);
                        cardUsed = true;
                    }

                    break;

                case KILL:
                    if (pigs[currentPig].equipped || !killUsed) {
                        int nx = next(currentPig, n);

                        if (pigs[currentPig].team == MASTER) {
                            if (intel[nx] == FAN || intel[nx] == LIKEFAN) {
                                use(currentPig, KILL, nx, n);
                                cardUsed = true;
                                killUsed = true;
                            }
                        } else if (pigs[currentPig].team == ZHONG) {
                            if (intel[nx] == FAN) {
                                use(currentPig, KILL, nx, n);
                                cardUsed = true;
                                killUsed = true;
                            }
                        } else {
                            if (intel[nx] == ZHONG || intel[nx] == MASTER) {
                                use(currentPig, KILL, nx, n);
                                cardUsed = true;
                                killUsed = true;
                            }
                        }

                        // end immediately
                        if (ended) {
                            return;
                        }
                    }

                    break;

                case NANZHU:
                    use(currentPig, NANZHU, currentPig, n);
                    cardUsed = true;

                    if (ended) {
                        return;
                    }

                    break;

                case WANJIAN:
                    use(currentPig, WANJIAN, currentPig, n);
                    cardUsed = true;

                    if (ended) {
                        return;
                    }

                    break;

                case FIGHT:
                    fightUsed = false;
                    fori(n - 1) {
                        int index = (i + currentPig + 1) % n;

                        if (!died[index]) {
                            if (pigs[currentPig].team == MASTER) {
                                if (intel[index] == FAN || intel[index] == LIKEFAN) {
                                    use(currentPig, FIGHT, index, n);
                                    fightUsed = true;
                                    cardUsed = true;
                                    break;
                                }
                            } else if (pigs[currentPig].team == ZHONG) {
                                if (intel[index] == FAN) {
                                    use(currentPig, FIGHT, index, n);
                                    fightUsed = true;
                                    cardUsed = true;
                                    break;
                                }
                            } else {
                                // primary
                                if (intel[index] == MASTER) {
                                    use(currentPig, FIGHT, index, n);
                                    fightUsed = true;
                                    cardUsed = true;
                                    break;
                                }
                            }
                        }
                    }

                    if (!fightUsed) {
                        if (pigs[currentPig].team == FAN) {
                            fori(n - 1) {
                                int index = (i + currentPig + 1) % n;

                                if (!died[index]) {
                                    // secondary
                                    if (intel[index] == ZHONG) {
                                        use(currentPig, FIGHT, index, n);
                                        fightUsed = true;
                                        cardUsed = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    if (ended) {
                        return;
                    }

                    break;

                case ZHUGE:
                    pigs[currentPig].equipped = true;
                    use(currentPig, ZHUGE);
                    cardUsed = true;
                    break;
            }

            if (cardUsed) {
                break;
            }
        }

        if (!cardUsed) {
            break;
        }
    }
}

// return is ji used
bool broadcastJi(int currentPig, bool malicious, int n) {
    fori(n) {
        int index = (i + currentPig) % n;

        if (!died[index]) {
            if (decideJi(index, currentPig, malicious, n)) {
                return true;
            }
        }
    }
    return false;
}

// return is ji used
bool decideJi(int currentPig, int target, bool malicious, int n) {
    if (pigs[currentPig].counts[JI] > 0) {
        if ((pigs[currentPig].team == MASTER ||
             pigs[currentPig].team == ZHONG) &&
            malicious ||
            pigs[currentPig].team == FAN &&
            !malicious) {
            use(currentPig, JI);

            if (malicious) {
                if (intel[currentPig] == UNKNOWN || intel[currentPig] == LIKEFAN) {
                    intel[currentPig] = ZHONG;
                }
            } else {
                if (intel[currentPig] == UNKNOWN || intel[currentPig] == LIKEFAN) {
                    intel[currentPig] = FAN;
                }
            }

            // recursive
            if (broadcastJi(currentPig, !malicious, n)) {
                return false;
            }
            return true;
        }
    }

    return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    // input

    int n;
    cin >> n >> m;

    fori(n) {
        string teamStr;
        cin >> teamStr;

        int team;
        team = fromTeam(teamStr);

        pigs[i].team = team;
        pigs[i].health = 4;
        pigs[i].equipped = false;

        intel[i] = team == 0 ? EXACT_MASTER : UNKNOWN;
        died[i] = false;

        memset(pigs[i].counts, 0, sizeof(int) * 8);

        forj(4) {
            char cardType;
            cin >> cardType;
            append(i, fromName(cardType));
        }
    }

    fori(m) {
        char cardType;
        cin >> cardType;

        pile[i] = fromName(cardType);
    }

    // cards before this ptr is not avaliable
    int pilePtr = 0;
    pilePtrPtr = &pilePtr;

    int currentPig = 0;

    while (!ended) {
        distribute(currentPig, pilePtrPtr);
        decide(currentPig, n);
        currentPig = next(currentPig, n);
    }

    // output
    cout << (masterWin ? "MP" : "FP") << endl;
    fori(n) {
        if (died[i]) {
            cout << "DEAD" << endl;
        } else {
            forj(pigs[i].count - 1) {
                cout << fromIndex(pigs[i].cards[j]) << ' ';
            }

            if (pigs[i].count == 0) {
                cout << endl;
            } else {
                cout << fromIndex(pigs[i].cards[pigs[i].count - 1]) << endl;
            }
        }
    }

    return 0;
}
posted @ 2022-12-01 21:34  叁纔  阅读(58)  评论(4)    收藏  举报