『Fwb』斗主纷争の题解

· · 个人记录

难度:普及+/提高。

方法:大模拟。

upd:赛时大家问地主是谁,看输入描述,底牌中不含 D

主要注意几个细节:

代码里有详细的注释:

#include <bits/stdc++.h>
using namespace std;
int sum[5][20];//sum[i][1,2,...,15]为 3,4,5,6,7,8,9,S(10),J,Q,K,A,2,X,D
int num[5] = {0, 17, 17, 17};//玩家的牌数 
int lst[20], flag, lst_id_print;
void add(char t, int x) {
    if ('3' <= t && t <= '9') sum[x][t - '2']++;
    else if (t == 'S') sum[x][8]++;
    else if (t == 'J') sum[x][9]++;
    else if (t == 'Q') sum[x][10]++;
    else if (t == 'K') sum[x][11]++;
    else if (t == 'A') sum[x][12]++;
    else if (t == '2') sum[x][13]++;
    else if (t == 'X') sum[x][14]++;
    else if (t == 'D') sum[x][15]++;
    return ;
}
char get(int id) {
    if (1 <= id && id <= 7) return '2' + id;
    else if (id == 8) return 'S';
    else if (id == 9) return 'J';
    else if (id == 10) return 'Q';
    else if (id == 11) return 'K';
    else if (id == 12) return 'A';
    else if (id == 13) return '2';
    else if (id == 14) return 'X';
    else if (id == 15) return 'D';
}
inline void print(int now, int id, int k) {
    if (k == -1) {
        printf("%d Next\n", now);
        return ;
    }
    printf("%d %c %d\n", now, get(id), k);
    return ;
}
inline void print_first(int now) {
    lst_id_print = now;//肯定是now出的牌 
    int chu = 0, zha = 0, id = 0;
    for (int i = 1; i <= 13; i++) {
        if (sum[now][i] == 4) {
            zha = 1;
            if (id == 0) id = i;
            continue;
        }
        if (sum[now][i] == 3) {
            int j;
            for (j = i + 1; j <= 15; j++) {
                if (sum[now][j] > 0) {
                    break;
                }
            }
            if (j == 16) {//出一张单牌 
                print(now, i, 1);
                lst[i]++;
                num[now]--;
                sum[now][i]--;
            } else {//三带一 
                print(now, i, 3);
                lst[i] += 3;
                print(now, j, 1);
                lst[j]++;
                num[now] -= 4;
                sum[now][i] = 0;
                sum[now][j]--;
            }
        } else if (sum[now][i] == 2) {
            print(now, i, 2);
            lst[i] += 2;
            num[now] -= 2;
            sum[now][i] = 0;
        } else if (sum[now][i] == 1) {
            print(now, i, 1);
            lst[i]++;
            num[now]--;
            sum[now][i] = 0;
        } else {
            continue;
        }
        chu = 1;
        break;//只要没有continue,就说明出牌了,break以免重复出牌 
    }
    if (chu == 0 && zha == 1) {
        print(now, id, 4);
        lst[id] += 4;
        num[now] -= 4;
        sum[now][id] = 0;
    } else if (chu == 0) {
        if (sum[now][14] == 1 && sum[now][15] == 1) {
            print(now, 14, 1);
            lst[14]++;
            print(now, 15, 1);
            lst[15]++;
            num[now] -= 2;
            sum[now][14] = 0;
            sum[now][15] = 0;
        }
    }
    return ;
}
inline bool get_next(int now) {//now这个人要接/不接上一个玩家的牌 
    if (lst_id_print != flag && now != flag) {//如果上一个人和我不是地主 
        print(now, 0, -1);
        return false;//队友不打队友 
    }
    //不是队友能打一定会打 
    int lst_aid = 0;
    for (int i = 1; i <= 15; i++) {
        if (lst[i] == 3) {
            lst_aid = i;//只要比i大就行,不用管三带一的带一 
            break;
        }
    }
    if (lst_aid == 0) {//不是三带一 
        for (int i = 1; i <= 15; i++) {
            if (lst[i] == 2) {
                lst_aid = i;
                break;
            }
        }
    }
    if (lst_aid == 0) {//不是三带一,不是对子 
        for (int i = 1; i <= 13; i++) {
            if (lst[i] == 4) {
                lst_aid = i;
                break;
            }
        }
    }
    if (lst_aid == 0) {//不是三带一,不是对子,不是炸弹 
        if (lst[14] == 1 && lst[15] == 1) {
            lst_aid = -1;
        }
    }
    if (lst_aid == 0) {//不是三带一,不是对子,不是炸弹,不是王炸,就是单牌 
        for (int i = 1; i <= 15; i++) {
            if (lst[i] == 1) {
                lst_aid = i;
                break;
            }
        }
    }
    if (lst_aid == -1) {//王炸管不了 
        print(now, 0, -1);
        return false;
    }
    int if_print = 0;//是否出牌 
    int lst_num = lst[lst_aid];
    int copy_lst[20];//保存上一轮的牌 
    for (int i = 1; i <= 15; i++) {
        copy_lst[i] = lst[i];
    }
    memset(lst, 0, sizeof(lst));
    for (int i = lst_aid + 1; i <= 15; i++) {
        if (sum[now][i] != lst_num) continue;
        if (lst_num == 3) {//需要带一,带最小的一个 
            int print_id = 0;
            for (int j = 1; j <= 15; j++) {
                if (i == j) continue;
                if (sum[now][j]) {
                    print_id = j;
                    break;
                }
            }
            if (print_id == 0) continue;
            //带print_id 
            num[now] -= 3;
            print(now, i, 3);//记得拆开输出 
            num[now]--;
            print(now, print_id, 1);
            lst[i] += 3;
            lst[print_id]++;
            if_print = 1;
            sum[now][i] = 0;
            sum[now][print_id]--;//记得出牌 
            lst_id_print = now;//记录是now管的牌 
            break;
        } else {//两张/一张/四张 
            num[now] -= lst_num;
            print(now, i, lst_num);
            lst[i] += lst_num;
            if_print = 1;
            sum[now][i] -= lst_num;//别忘记出牌 
            lst_id_print = now;
            break;
        }
    }
    if (if_print == 1) return true;
    //没有出牌成功 
    //原因:1.对面不是炸,但我有炸 2.对面不是炸,但我有王炸 3.对面是炸,我有王炸 4.真管不了 
    if (lst_num == 4) {//对面是炸,注意不是三带一,因为如果是三带一lst_num为3 
        if (sum[now][14] == 1 && sum[now][15] == 1) {//我有王炸 
            //不用再判断我有没有大于他的炸,因为如果有上面就出掉了 
            print(now, 14, 1);
            print(now, 15, 1);
            sum[now][14]--;
            sum[now][15]--;
            lst[14]++, lst[15]++;
            num[now] -= 2;
            lst_id_print = now;
            return true;
        } else {
            for (int i = 1; i <= 15; i++) {
                lst[i] = copy_lst[i];//回溯 
            }
            print(now, 0, -1);
            return false;
        }
    } else {//对面不是炸,且不是王炸,判断我有没有炸或者王炸 
        for (int i = 1; i <= 13; i++) {
            if (sum[now][i] == 4) {//我有炸,不管多大都能炸掉对面的非炸弹牌 
                print(now, i, 4);
                lst[i] += 4;
                num[now] -= 4;
                sum[now][i] = 0;
                lst_id_print = now;
                return true;
            }
        }
        if (sum[now][14] == 1 && sum[now][15] == 1) {//我有王炸 
            print(now, 14, 1);
            print(now, 15, 1);
            sum[now][14]--;
            sum[now][15]--;
            lst[14]++, lst[15]++;
            num[now] -= 2;
            lst_id_print = now;
            return true;
        } else {//我又没有炸,也没有王炸,还管不了牌,就跳过 
            for (int i = 1; i <= 15; i++) {
                lst[i] = copy_lst[i];
            }
            print(now, 0, -1);
            return false;
        }
    }
}
inline int solve(int now) {
    memset(lst, 0, sizeof(lst));
    print_first(now);
    if (num[now] == 0) {
        return now;
    }
    int player = now + 1;
    if (player == 4) player = 1;
    int no_add = 0;
    while (no_add < 2) {
        if (get_next(player) == false) {
            player++;
            if (player == 4) player = 1;
            no_add++;
            continue;
        }
        if (num[player] == 0) {
            return 0;
        }
        player++;
        if (player == 4) player = 1;
        no_add = 0;
    }
    return player;//已经++了,不用再+1,现在的player就是下一个是谁的回合 
}
int main() {
    for (int i = 1; i <= 3; i++) {
        string s;
        cin >> s;
        for (int j = 0; j < (int)s.size(); j++) {
            add(s[j], i);
            if (s[j] == 'D') flag = i;
        }
    }
    char x, y, z;
    cin >> x >> y >> z;
    add(x, flag), add(y, flag), add(z, flag);
    num[flag] += 3;
    int hui = flag;//谁的回合 
    while (num[1] > 0 && num[2] > 0 && num[3] > 0) {
        hui = solve(hui);
    }
    if (num[flag] == 0) printf("D");
    else printf("P");
    return 0;
}

希望大家写的开心,我是喜欢大模拟的。