1 条题解
-
0
题意
- 进行 T 组询问
- 每组询问,给定 n 张牌,问是否可以两次以内出完(一次或者两次)
- 每次出牌有三种牌型可以选择
- 顺子:选择从小到大连续的至少 3 张牌打出
- 三带二:选择一种牌 a 和另一种牌 b,打出 3 张 a 和 2 张 b
- 四带一一:选择一种牌 a 和另一种牌 b,以及与 a b 不同的牌 c,打出 4 张 a、1 张 b 和 1 张 c
- 如果可以两次内出完,输出
Yes,按格式输出每次打出牌的类型- 输出第一行一个整数 1 或者 2,表示出牌次数
- 接着输出每次出牌的内容
- 顺子,输出
S表示顺子,然后输出m a1 a2 ... am表示出了 m 张牌作为顺子且他们从小到大依次是哪些 - 三带二,输出
T表示三带二,然后输出a b表示出了 3 张 a 和 2 张 b - 四带一一,输出
F表示四带一一,然后输出a b c表示出 4 张 a、1 张 b 和 1 张 c
- 如果不可以两次内出完,输出
No - 值得注意的是有 13 种牌
2 3 4 5 6 7 8 9 T J Q K A依次代表点数2 ~ 14(其中A不能表示 1) - 且不会有某种牌出现次数超过 4
思路
- 没什么思路,直接模拟
数据范围
- 对于测试点
1 ~ 15(30 分)- 数据保证有解,且存在一次出完的方式
- 思路为,直接判断是哪种牌型,组织对应牌型输出即可
- 对于测试点
12 ~ 27- 没有特殊解法,可以用于检测哪种牌型判断错误
题解
- 实际上总共只有 13 种牌
- 所以顺子只有 种牌型
- 三带二只有 种牌型
- 四带一一有 种牌型
- 这里合法的方案只有
1080种
- 所以可以有一个 的做法
- 枚举第一种牌型
- 然后枚举第二种牌型
- 最后判定两种牌型的选取牌的集合是否恰好为输入的牌
- 能够获得 分,因为效率问题,最后 10 分得不到
- 效率的解决方式
- 方式一(下面 code 的方式)
- 对于所有的一次和两次的合法出法,提前计算它的答案
- 然后对于输入直接在预处理的答案中查询即可
- 时间复杂度:$O(1080^2\times 13 \times \log 1080^2 + T\times n \times \log 1080^2)$
- 实际上中间的 13 和 n 倍数是卡在字符串比较上,但事实上并不会全量比较(只会前缀比较)
- 方法二
- 提前只预处理出一次合法的情况,提前计算它的答案
- 对于每次询问,先枚举上面的所有 1080 种合法情况,扣除这些牌
- 在检查剩下的牌是否是合法情况即可
- 时间复杂度:$O(1080\times 13 \times \log 1080 + T\times (1080 \times 13 + 13 \times \log 1080))$
- 方式一(下面 code 的方式)
code
map<string, string> ma; void solved() { int n; cin >> n; string s; _for(i, 1, n) { char c; cin >> c; s += c; } sort(s.begin(), s.end()); if(ma[s] == "") cout << "No" << endl; else cout << ma[s]; return ; } string hs[13 * 13 * 2 + 13 * 13 * 13 + 9]; int tot; string ask(string s) { string ans = ""; if(s[0] == '*') { ans = "S " + to_string(s.size() - 1); _for(i, 1, s.size() - 1) ans = ans + " " + s[i]; } else if(s.size() == 5) ans = ans + "T " + s[0] + " " + s[4]; else ans = ans + "F " + s[0] + ' ' + s[4] + ' ' + s[5]; ans += '\n'; return ans; } void init() { string s = " 23456789TJQKA"; int n = 14; _for(l, 2, n) _for(r, l + 2, n) { string now = "*"; _for(i, l, r) now += s[i]; hs[++ tot] = now; } _for(i, 2, n) _for(j, 2, n) if(i != j) { string now = ""; _for(k, 1, 3) now += s[i]; _for(k, 1, 2) now += s[j]; hs[++ tot] = now; } _for(i, 2, n) _for(j, 2, n) _for(k, j + 1, n) if(i != j && i != k) { string now = ""; _for(t, 1, 4) now += s[i]; now += s[j], now += s[k]; hs[++ tot] = now; } _for(i, 1, tot) { string now = ""; if(hs[i][0] == '*') now = hs[i].substr(1); else now = hs[i]; sort(now.begin(), now.end()); string ans = "Yes\n1\n"; ma[now] = ans + ask(hs[i]); } _for(i, 1, tot) _for(j, i, tot) { string now = ""; if(hs[i][0] == '*') now += hs[i].substr(1); else now += hs[i]; if(hs[j][0] == '*') now += hs[j].substr(1); else now += hs[j]; sort(now.begin(), now.end()); string ans = "Yes\n2\n" + ask(hs[i]) + ask(hs[j]); ma[now] = ans; } } signed main() { IOS PCS(6) init(); int ttx; cin >> ttx; _for(i, 1, ttx) solved(); return 0; }另一种做法
#include <bits/stdc++.h> using namespace std; typedef array<int, 13> card; string id = "23456789TJQKA"; map<card, string> mp; int main() { ios::sync_with_stdio(0); cin.tie(0); for (int m = 0; m < 13 * 13 * 13 * 3; m++) { int type = m / 2197; int r = m % 2197; int i = r % 13; int j = (r / 13) % 13; int k = r / 169; if (type == 0 && i + j + 3 <= 13 && !k) { card h{}; string s = "S " + to_string(j + 3); for (int x = 0; x < j + 3; x++) { h[i + x] = 1; s += string(" ") + id[i + x]; } mp[h] = s; } if (type == 1 && i != j && !k) { card h{}; h[i] = 3; h[j] = 2; mp[h] = string("T ") + id[i] + " " + id[j]; } if (type == 2 && i != j && i != k && j < k) { card h{}; h[i] = 4; h[j] = 1; h[k] = 1; mp[h] = string("F ") + id[i] + " " + id[j] + " " + id[k]; } } int T; cin >> T; while (T--) { int n; cin >> n; card c{}; for (int i = 0; i < n; i++) { char ch; cin >> ch; c[id.find(ch)]++; } if (mp.count(c)) { cout << "Yes\n1\n" << mp[c] << "\n"; continue; } string s1 = ""; for (auto p : mp) { auto r = c; bool ok = 1; for (int i = 0; i < 13; i++) { if ((r[i] -= p.first[i]) < 0) { ok = 0; } } if (ok && mp.count(r)) { s1 = p.second + "\n" + mp[r]; break; } } if (s1 != "") { cout << "Yes\n2\n" << s1 << "\n"; } else { cout << "No\n"; } } }
- 1
信息
- ID
- 159
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 18
- 已通过
- 2
- 上传者