『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;
}
希望大家写的开心,我是喜欢大模拟的。