欧拉计划 Project Euler54(扑克手牌)题解
欧拉计划 Project Euler 54 题解
- 题干
- 🥇 胜负判定规则
- 🎴 示例对局
- 📄 数据说明
- ✅ 问题目标
- 思路
- code
题干
在扑克游戏中,玩家的手牌由五张牌组成,其等级由低到高分别为:
- 单牌:牌面最大的一张牌。
- 对子:两张牌面一样的牌。
- 两对:两个不同的对子。
- 三条:三张牌面一样的牌。
- 顺子:五张牌的牌面是连续的。
- 同花:五张牌是同一花色。
- 葫芦:三条带一个对子。
- 四条:四张牌面一样的牌。
- 同花顺:五张牌的牌面是连续的且为同一花色。
- 皇家同花顺:同一花色的 10、J、Q、K、A。
牌面从小到大的顺序为:2、3、4、5、6、7、8、9、10、J、Q、K、A。
🥇 胜负判定规则
- 若两名玩家的手牌等级不同,等级高者胜。
- 若手牌等级相同,则比较组成该等级的牌中最大的牌;
- 若继续相同,比较剩余牌的大小,直到区分胜负。
🎴 示例对局
手牌编号 | 玩家 1 | 玩家 2 | 说明 | 胜者 |
---|---|---|---|---|
1 | 红桃5 草花5 黑桃6 黑桃7 方片K | 草花2 黑桃3 黑桃8 方片8 方片10 | 一对8 > 一对5 | 玩家2 |
2 | 方片5 草花8 黑桃9 黑桃J 草花A | 草花2 草花5 方片7 黑桃8 红桃Q | A > Q | 玩家1 |
3 | 方片2 草花9 黑桃A 红桃A 草花A | 方片3 方片6 方片7 方片10 方片Q | 同花 > 三条 | 玩家2 |
4 | 方片4 黑桃6 红桃9 红桃Q 草花Q | 方片3 方片6 红桃7 方片Q 黑桃Q | 一对Q,最大单牌 9 > 7 | 玩家1 |
5 | 红桃2 方片2 草花4 方片4 黑桃4 | 草花3 方片3 黑桃3 黑桃9 方片9 | 葫芦(三条4 > 三条3) | 玩家1 |
📄 数据说明
在文件 poker.txt 中,共包含 1,000 局扑克游戏:
- 每行代表一局;
- 每行有 10 张牌,前 5 张是 玩家 1 的手牌,后 5 张是 玩家 2 的手牌;
- 所有手牌都是有效的且不重复;
- 每局都有明确的赢家。
✅ 问题目标
统计:在这 1000 局游戏中,玩家 1 获胜的局数。
思路
这个题是个大模拟题,注意细节不要漏掉A5顺子,A可以当1使用,具体实现看代码,给了大量注释
- 解析每行牌,分配给两个玩家;
- 分别计算双方手牌的牌型与比较值;
- 判断谁胜出并统计玩家1的胜场次数。
code
// ans = 376
#include <bits/stdc++.h>using namespace std;using ll = long long;// 扑克牌面值的映射关系
// poker.txt 中的10是用T表示的
map<char, int> value_map = {{'2', 2}, {'3', 3}, {'4', 4}, {'5', 5}, {'6', 6},{'7', 7}, {'8', 8}, {'9', 9}, {'T', 10}, {'J', 11},{'Q', 12}, {'K', 13}, {'A', 14}
};// 表示一张牌
struct Card {int value; // 牌的点数,例如 2-14(A 是 14)char suit; // 牌的花色,例如 'H'、'D'、'C'、'S' 红桃 方块 梅花 黑桃
};// 比较两张牌的大小 从大到小排序
bool cmp(const Card &a, const Card &b) {return a.value > b.value;
}// 将字符串形式的牌转换为结构体形式并排序
vector<Card> parse_hand(const vector<string> &str_hand) {vector<Card> hand;for (const string &s : str_hand) {hand.push_back({value_map[s[0]], s[1]});}sort(hand.begin(), hand.end(), cmp);return hand;
}// 评估手牌的等级和关键的点数 用于后续的比较
pair<int, vector<int>> evaluate_hand(const vector<Card> &hand) {vector<int> values;map<int, int> counts;set<char> suits;// 统计每张牌的点数和花色for (const auto &card : hand) {values.push_back(card.value);counts[card.value]++;suits.insert(card.suit);}sort(values.begin(), values.end(), greater<int>());// 根据点数统计频次并排序// 优先比较数量多的 再比较点数大的vector<pair<int, int>> freq;for (auto &[val, cnt] : counts) {freq.push_back({cnt, val});}sort(freq.begin(), freq.end(), [](auto &a, auto &b){return a.first == b.first ? a.second > b.second : a.first > b.first;});bool flush = suits.size() == 1; // 同花// 顺子bool straight = false;// 检查一般的顺子if (values[0] - values[4] == 4 && set<int>(values.begin(), values.end()).size() == 5) {straight = true;} else {// 检查 A-5 顺子 (A, 5, 4, 3, 2)vector<int> ace_low_straight_values = {14, 5, 4, 3, 2};vector<int> current_values_sorted = values;sort(current_values_sorted.begin(), current_values_sorted.end());vector<int> values_for_ace_low = {2, 3, 4, 5, 14};sort(values_for_ace_low.begin(), values_for_ace_low.end());if (current_values_sorted == values_for_ace_low) {straight = true;// 为了让 A-5 顺子在比较时被正确处理,将其最高牌视为 5values = {5, 4, 3, 2, 1}; // 使用 1 来表示 A 作为最低牌的情况}}// 依次判断每个等级 从高到低// 皇家同花顺if (flush && straight && values[0] == 14) return {10, {}};// 同花顺if (flush && straight) return {9, {values[0]}};// 四条if (freq[0].first == 4) return {8, {freq[0].second, freq[1].second}};// 葫芦if (freq[0].first == 3 && freq[1].first == 2) return {7, {freq[0].second, freq[1].second}};// 同花if (flush) return {6, values};// 顺子if (straight) {if (values[0] == 5 && values[4] == 1) return {5, {5}}; // A-5 straight represented by 5return {5, {values[0]}};}// 三条if (freq[0].first == 3) {vector<int> result = {freq[0].second};for(const auto& val : values) {if (val != freq[0].second) {result.push_back(val);}}return {4, result};}// 两对if (freq[0].first == 2 && freq[1].first == 2) {vector<int> result = {freq[0].second, freq[1].second};for(const auto& val : values) {if (val != freq[0].second && val != freq[1].second) {result.push_back(val);break;}}return {3, result};}// 对子if (freq[0].first == 2) {vector<int> result = {freq[0].second};int kickers_count = 0;for(const auto& val : values) {if (val != freq[0].second && kickers_count < 3) {result.push_back(val);kickers_count++;}}return {2, result};}return {1, values};
}bool player1_wins(const vector<string> &cards) {vector<string> p1(cards.begin(), cards.begin() + 5);vector<string> p2(cards.begin() + 5, cards.end());auto hand1 = evaluate_hand(parse_hand(p1));auto hand2 = evaluate_hand(parse_hand(p2));return hand1 > hand2;
}void solve() {ifstream file("poker.txt");string line;int ans = 0;while (getline(file, line)) {istringstream iss(line);vector<string> cards(10);for (int i = 0; i < 10; ++i) {iss >> cards[i];}if (player1_wins(cards)) ans++;}cout << ans << "\n";}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int tt = 1; // cin >> tt;while (tt--) {solve();}return 0;
}