飞飞很喜欢打牌,他决定苦练牌技,终成赌神!
飞飞有 A × B 张扑克牌。每张扑克牌有一个大小(整数,记为a,范围区间是 0 到 A - 1)和一个花色(整数,记为b,范围区间是 0 到 B - 1。
扑克牌是互异的,也就是独一无二的,也就是说没有两张牌大小和花色都相同。
“一手牌”的意思是你手里有5张不同的牌,这 5 张牌没有谁在前谁在后的顺序之分,它们可以形成一个牌型。 我们定义了 9 种牌型,如下是 9 种牌型的规则,我们用“低序号优先”来匹配牌型,即这“一手牌”从上到下满足的第一个牌型规则就是它的“牌型编号”(一个整数,属于1到9):
- 同花顺: 同时满足规则 5 和规则 4.
- 炸弹 : 5张牌其中有4张牌的大小相等.
- 三带二 : 5张牌其中有3张牌的大小相等,且另外2张牌的大小也相等.
- 同花 : 5张牌都是相同花色的.
- 顺子 : 5张牌的大小形如 x, x + 1, x + 2, x + 3, x + 4
- 三条: 5张牌其中有3张牌的大小相等.
- 两对: 5张牌其中有2张牌的大小相等,且另外3张牌中2张牌的大小相等.
- 一对: 5张牌其中有2张牌的大小相等.
- 要不起: 这手牌不满足上述的牌型中任意一个.
现在, 飞飞从 A × B 张扑克牌中拿走了 2 张牌!分别是 (a1, b1) 和 (a2, b2). (其中a表示大小,b表示花色)
现在要从剩下的扑克牌中再随机拿出 3 张!组成一手牌!!
其实飞飞现在要预言他的未来的可能性,即他将拿到的“一手牌”的可能性,我们用一个 “牌型编号(一个整数,属于1到9)” 来表示这手牌的牌型,那么他的未来有 9 种可能,但每种可能的方案数不一样。
现在飞飞想要计算一下 9 种牌型中,每种牌型的方案数。
Input
第 1 行包含了整数 A 和 B (1 ≤ A ≤ 25, 1 ≤ B ≤ 4).
第 2 行包含了整数 a1, b1, a2, b2 (0 ≤ a1, a2 ≤ A - 1, 0 ≤ b1, b2 ≤ B - 1, (a1, b1) ≠ (a2, b2)).
Output
输出一行,这行有 9 个整数,每个整数代表了 9 种牌型的方案数(按牌型编号从小到大的顺序)
限制:设计程序进行枚举,不能使用数学公式推导出每种牌型的方案数式子!
限制:设计程序进行枚举,不能使用数学公式推导出每种牌型的方案数式子!
限制:设计程序进行枚举,不能使用数学公式推导出每种牌型的方案数式子!
测试用例
1
输入
5 2
1 0 3 1
输出
0 0 0 0 8 0 12 36 0
2
输入
25 4
0 0 24 3
输出
0 2 18 0 0 644 1656 36432 113344
#include <bits/stdc++.h>
using namespace std;
//卡牌
struct card {
int dx = 0; //大小
int hs = 0; //花色
card() {}
card(const int& d, const int& h) { //参数分别是大小、花色
dx = d;
hs = h;
}
bool operator==(const card& c) const {
return dx == c.dx; //重载==
}
bool operator<(const card& c) const {
return dx < c.dx; //重载<
}
card operator+(const int& c) const {
return card(dx + c, hs); //重载+
}
};
//分别是每种牌型的方案数
int f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, f6 = 0, f7 = 0, f8 = 0, f9 = 0;
card a[5]; //储存手里的5张牌
vector<card> va; //储存所有的牌
//判断当前手牌是否为炸弹
bool zadan() {
if (a[0] == a[1] && a[1] == a[2] && a[2] == a[3]) //前4张大小相同
return true;
if (a[1] == a[2] && a[2] == a[3] && a[3] == a[4]) //或者后4张大小相同
return true;
return false;
}
//判断当前手牌是否为同花,即5张的花色是否相同
bool tonghua() {
return a[0].hs == a[1].hs && a[1].hs == a[2].hs && a[2].hs == a[3].hs && a[3].hs == a[4].hs;
}
//判断当前手牌是否为三带二
bool sandaier() {
if (a[1] == a[2]) //前三张相同、后两张相同
return a[0] == a[1] && a[3] == a[4];
if (a[2] == a[3]) //前两张相同、后三张相同
return a[0] == a[1] && a[3] == a[4];
return false;
}
//判断当前手牌是否为顺子
bool sunzi() {
if (a[0] + 1 == a[1]) //大小依次加一
if (a[1] + 1 == a[2])
if (a[2] + 1 == a[3])
if (a[3] + 1 == a[4])
return true;
return false;
}
//判断当前手牌是否为三条
bool santiao() { // 3种情况:前三张、或者中间三张、或者后面三张相同
return (a[0] == a[1] && a[1] == a[2]) || (a[1] == a[2] && a[2] == a[3]) || (a[2] == a[3] && a[3] == a[4]);
}
//判断当前手牌是否为两对
bool liangdui() { // 3种情况:前面一对中间一对、前面一对后面一对、中间一对后面一对
return (a[0] == a[1] && (a[2] == a[3] || a[3] == a[4])) || (a[1] == a[2] && a[3] == a[4]);
}
//判断当前手牌是否为一对
bool yidui() {
return a[0] == a[1] || a[1] == a[2] || a[2] == a[3] || a[3] == a[4];
}
//判断当前手牌是否为同花顺
bool tonghuasun() {
return tonghua() && sunzi();
}
//判断当前手牌的牌型,并且统计该牌型的频率
void pandaun() { //先手牌按照大小排序,便于判断炸弹、三带二、顺子、同花顺、三条两对、一对
sort(a, a + 5);
if (tonghuasun()) //如果是同花顺
f1++;
else if (zadan()) //如果是炸弹
f2++;
else if (sandaier()) //如果是三带二
f3++;
else if (tonghua()) //如果是同花
f4++;
else if (sunzi()) //如果是顺子
f5++;
else if (santiao()) //如果是三条
f6++;
else if (liangdui()) //如果是两对
f7++;
else if (yidui()) //如果是一对
f8++;
else //如果是要不起
f9++;
}
int main() {
int da, db, ta1, tb1, ta2, tb2;
cin >> da >> db; //整数A和B
cin >> ta1 >> tb1 >> ta2 >> tb2; //整数a1、b1、a2、b2
for (int i = 0; i < da; ++i) { //创建AXB张牌
for (int j = 0; j < db; ++j) {//如果这两张牌已经选了就不加入到vector
if (!((i == ta1 && j == tb1) || (i == ta2 && j == tb2)))
va.emplace_back(i, j);
}
}
int n = da * db - 2;
card ka1(ta1, tb1), ka2(ta2, tb2); //已经选了的两张牌
for (int i = 0; i < n - 2; ++i) //从剩下的牌选1张
for (int j = i + 1; j < n - 1; ++j) //再从剩下的牌选1张
for (int k = j + 1; k < n; ++k) { //再再从剩下的牌选1张
a[0] = ka1; //将当前选择的5张牌放入数组
a[1] = ka2;
a[2] = va[i];
a[3] = va[j];
a[4] = va[k];
pandaun(); //判断当前牌型并统计
}
cout << f1 << ' ' << f2 << ' ' << f3 << ' ' << f4 << ' ' << f5 << ' ' << f6 << ' ' << f7 << ' ' << f8 << ' ' << f9 << ' ';
return 0;
}