题目描述
从瑞神家打牌回来后,东东痛定思痛,决定苦练牌技,终成赌神!
东东有 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 (5 ≤ 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 种牌型的方案数(按牌型编号从小到大的顺序)
Sample Input 1
5 2
1 0 3 1
Sample Output 1
0 0 0 0 8 0 12 36 0
Sample Input 2
25 4
0 0 24 3
Sample Output 2
0 2 18 0 0 644 1656 36432 113344
算法/思路分析
题目分析:本题牌最多有25*4张,已抽2张牌,再抽3张牌,即最多有C(98,3)= 152096种,因此可以直接模拟/暴力/枚举。
数据结构:卡片信息直接通过结构体数组c[A*B]记录,用结构体数组d[1]…d[5]记录每一种类型的5张牌,num[1],…,num[9]记录9种类型的数量。
判断类型:观察9个类型,除了类型四(同花)、类型九(要不起)外,均和牌大小相等的数量有关。因此可从此处入手,维护cnum[26]记录每一次组合大小为i的牌的数量。根据cnum[i] == x及其他辅助条件判断类型,具体见代码部分。
本题需要注意以下问题/细节:
(1)O(n3)枚举时,可通过vis[i] [j] [k]记录已经记录的组合,从而实现记忆化搜索防止重复检测同一种组合。(例如:(1,2,3)(1,3,2)(2,1,3)(2,3,1)(3,1,2)(3,2,1)是同一种组合)
(2)每一个组合检测类型时,要优先初始化并重新记录cnum[0],…,cnum[A-1].
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
using namespace std;
const int maxn = 187;
int A,B;
int num[10] = {0};
struct card
{
int a;
int b;
}c[maxn],d[6];
int vis[maxn][maxn][maxn] = {0}; //记忆化搜索
bool operator==(struct card c1,struct card c2)
{
if(c1.a == c2.a && c1.b == c2.b) return true;
else return false;
}
int cnum[26]; //记录大小为i的牌有多少个
//同花
bool type4()
{
if(d[1].b == d[2].b && d[2].b == d[3].b && d[3].b == d[4].b && d[4].b == d[5].b)
return true;
return false;
}
//顺子
bool type5()
{
for(int i = 0; i < A-4; i++)
if(cnum[i] == 1)
{
if(cnum[i+1] == 1 && cnum[i+2] == 1 && cnum[i+3] == 1
&& cnum[i+4] == 1)
return true;
}
return false;
}
//同花顺
bool type1()
{
if(type4() && type5())
return true;
return false;
}
//炸弹
bool type2()
{
for(int i = 0; i < A;i++)
if(cnum[i] == 4) return true;
return false;
}
//三带二
bool type3()
{
for(int i = 0; i < A;i++)
{
for(int j = 0; j < A; j++)
{
if(i == j) continue;
if(cnum[i] == 3 && cnum[j] == 2) return true;
else if(cnum[i] == 2 && cnum[j] == 3) return true;
}
}
return false;
}
//三条
bool type6()
{
for(int i = 0; i < A;i++)
if(cnum[i] == 3) return true;
return false;
}
//两对
bool type7()
{
for(int i = 0; i < A;i++)
{
if(cnum[i] == 2 && i < A-1)
{
for(int j = i + 1; j < A; j++)
if(cnum[j] == 2) return true;
}
}
return false;
}
//一对
bool type8()
{
for(int i = 0; i < A;i++)
if(cnum[i] == 2) return true;
return false;
}
//计数
void count()
{
//每次计数要重新统计cnum数组
for(int i = 0; i < A; i++) cnum[i] = 0;
for(int i = 1; i <= 5; i++) cnum[d[i].a]++;
//类型检测
if(type1()) num[1]++;
else if(type2()) num[2]++;
else if(type3()) num[3]++;
else if(type4()) num[4]++;
else if(type5()) num[5]++;
else if(type6()) num[6]++;
else if(type7()) num[7]++;
else if(type8()) num[8]++;
else num[9]++;
}
int main()
{
scanf("%d%d",&A,&B);
//初始化牌库
for(int i = 0; i < A; i++)
for(int j = 0; j < B; j++)
{
c[i*B+j].a = i;
c[i*B+j].b = j;
}
//拿两张牌
for(int i = 1; i <= 2;i++)
scanf("%d%d",&d[i].a,&d[i].b);
int sum = A*B;
for(int i = 0; i < sum; i++)
{
//不拿已经拿的牌
if(c[i] == d[1] || c[i] == d[2]) continue;
d[3] = c[i];
for(int j = 0; j < sum; j++)
{
//不拿已经拿的牌
if(c[j] == d[1] || c[j] == d[2] || c[j] == d[3]) continue;
d[4] = c[j];
for(int k = 0; k < sum; k++)
{
//不拿已经拿的牌
if(c[k] == d[1] || c[k] == d[2] || c[k] == d[3] || c[k] == d[4])
continue;
d[5] = c[k];
//记忆化搜索:拿过的组合不需要再拿
if(vis[i][j][k] || vis[i][k][j] || vis[j][i][k]
|| vis[j][k][i] || vis[k][i][j] || vis[k][j][i])
continue;
vis[i][j][k] = 1;
//检测类型并计数
count();
}
}
}
//输出结果
for(int i = 1; i <= 9; i++)
{
if(i > 1) printf(" ");
printf("%d",num[i]);
}
printf("\n");
return 0;
}