程序设计——第六周限时训练(暴力穷举:一手牌问题)

A-一手牌的牌型可能性

题目描述:

从瑞神家打牌回来后,东东痛定思痛,决定苦练牌技,终成赌神!
东东有 A × B 张扑克牌。每张扑克牌有一个大小(整数,记为a,范围区间是 0 到 A - 1)和一个花色(整数,记为b,范围区间是 0 到 B - 1。
扑克牌是互异的,也就是独一无二的,也就是说没有两张牌大小和花色都相同。
“一手牌”的意思是你手里有5张不同的牌,这 5 张牌没有谁在前谁在后的顺序之分,它们可以形成一个牌型。 我们定义了 9 种牌型,如下是 9 种牌型的规则,我们用“低序号优先”来匹配牌型,即这“一手牌”从上到下满足的第一个牌型规则就是它的“牌型编号”(一个整数,属于1到9):
同花顺: 同时满足规则 2 和规则 3.
顺子 : 5张牌的大小形如 x, x + 1, x + 2, x + 3, x + 4
同花 : 5张牌都是相同花色的.
炸弹 : 5张牌其中有4张牌的大小相等.
三带二 : 5张牌其中有3张牌的大小相等,且另外2张牌的大小也相等.
两对: 5张牌其中有2张牌的大小相等,且另外3张牌中2张牌的大小相等.
三条: 5张牌其中有3张牌的大小相等.
一对: 5张牌其中有2张牌的大小相等.
要不起: 这手牌不满足上述的牌型中任意一个.

现在, 东东从A × B 张扑克牌中拿走了 2 张牌!分别是 (a1, b1) 和 (a2, b2). (其中a表示大小,b表示花色)
现在要从剩下的扑克牌中再随机拿出 3 张!组成一手牌!!
其实东东除了会打代码,他业余还是一个魔法师,现在他要预言他的未来的可能性,即他将拿到的“一手牌”的可能性,我们用一个“牌型编号(一个整数,属于1到9)”来表示这手牌的牌型,那么他的未来有 9 种可能,但每种可能的方案数不一样。
现在,东东的阿戈摩托之眼没了,你需要帮他算一算 9 种牌型中,每种牌型的方案数。

思路分析:

做这道题首先要理解清除题意,题目描述中加粗的字要好好揣摩,题干其他部分都很容易理解。
拿到这个题,首先会想到的是排列组合,但是排列组合是真的不适合解答这道题,因为排列组合考虑的情况下很多,而且很少脑,这根本没有好好利用代码的能力。言归正传,这道题到底该怎么做呢?
第一步:选出剩下的三张牌,这道题的时限2000ms,测试数据A最大为25,B最大为4,所以如果暴力穷举去选出剩下三张牌的所有可能性的复杂度是O((A*B)^3),这样的复杂度是完全可以接受的。把选出的牌的值和花色分别用数组val和ch存储下来(我这里是从索引1开始存储)。要注意的一点就是选牌的时候,要避免重复选牌,也就是牌的值和花色不能同时与已经被选出的某张牌相同。
第二步:判断牌型。我只需要在选出的这五张牌中进行判断牌型就好。这里有个小技巧,就是把val和ch分别进行从小到大的排序。

(1)同花顺:(2)和(3)同时满足。
(2)顺子:只关注牌的值不管花色,也就是只看已经排好序的val数组,只要这个数组满足val[i]-val[i-1] =1(2<=i<=5)就说明这个数组中5张牌的值是连续的。
(3)同花:只看花色不看值,只关注ch数组,同花要求五张牌花色一致,也就是ch中五个值相等,由于ch已经排好序,所以如果ch[1]=ch[5]成立就说明5张牌同花色了。
(4)炸弹:只看牌值不看花色,只看排好序的val,炸弹要求四张牌大小相等,如果是炸弹,那么在排好序的val中一定有连着四个数字相等,要么是val[1].val[2].val[3].val[4],要么是val[2],val[3],val[4],val[5]。
(6)三带二:只看val数组值,val数组中一定又连续两个数字相等,另外连续三个数字相等,要么就是val[1]=val[2]=val[3]&&val[4]=val[5],要么就是val[1]=val[2]&&val[3]=val[4]=val[5]。
(7)~(8)的分析也类似,就不细说了。
注意!!!:最后的结果一定要除以6,因为穷举选择三张牌的时候,我只考虑到了不能选重复牌,但是!!!有可能不同循环下选中了已经在之前的循环下被选中的三张牌了,只是三张牌被选的顺序不同而已。

代码实现:

#include<cstdio>
#include<algorithm> 
using namespace std;

int a,b,a1,b1,a2,b2;
int val[6],ch[6],res[10]={0};//5张牌 ,9种可能性	

//先暴力枚举出一手牌的所有组合,分别从小到大存储下值和花色 

bool shunzi_2()
{
	for(int i=2;i<=5;i++)
	    if(val[i]-val[i-1]!=1)
	        return false;
	return true;
}

bool tonghua_3()
{
	if(ch[1]==ch[5])
	    return true;
	return false;
}

bool tonghuashun_1()
{
	if(shunzi_2()&&tonghua_3())
	    return true;
	return false;
}

bool zhadan_4()
{
	if(val[1]==val[4]||val[2]==val[5])
	    return true;
	return false;	
}

bool sandaier_5()
{
	if(val[1]==val[3]&&val[4]==val[5])
	    return true;
	if(val[1]==val[2]&&val[3]==val[5])
	    return true;
	return false;
}

bool liangdui_6()
{
	if(val[1]==val[2]&&val[3]==val[4])
	    return true;
	if(val[1]==val[2]&&val[4]==val[5])
	    return true;
	 if(val[2]==val[3]&&val[4]==val[5])
	    return true;
	return false;
}

bool santiao_7()
{
	if(val[1]==val[3]||val[2]==val[4]||val[3]==val[5])
	    return true;
	return false;
}

bool yidui_8()
{
	if(val[1]==val[2]||val[2]==val[3]||val[3]==val[4]||val[4]==val[5])
	    return true;
	return false;
}

int main()
{
	scanf("%d%d",&a,&b);
	scanf("%d%d%d%d",&a1,&b1,&a2,&b2);//接下来选出三张牌 
	
	for(int i3=0;i3<a;i3++)
	    for(int j3=0;j3<b;j3++)
	    
	        if((i3!=a1||j3!=b1)&&(i3!=a2||j3!=b2))
	            for(int i4=0;i4<a;i4++)
	                for(int j4=0;j4<b;j4++)
	                
	                   if((i4!=a1||j4!=b1)&&(i4!=a2||j4!=b2)&&(i4!=i3||j4!=j3))
	                        for(int i5=0;i5<a;i5++)
	                            for(int j5=0;j5<b;j5++)
	                                if((i5!=a1||j5!=b1)&&(i5!=a2||j5!=b2)&&(i5!=i3||j5!=j3)&&(i5!=i4||j5!=j4))
	                                {
	                                	val[1]=a1;val[2]=a2;val[3]=i3;val[4]=i4;val[5]=i5;//数组的赋值必须在循环内做!!!
	                                    ch[1]=b1;ch[2]=b2;ch[3]=j3;ch[4]=j4;ch[5]=j5;
	                                	sort(val+1,val+6);
	                                	sort(ch+1,ch+6);
	                                	if(tonghuashun_1()) res[1]++;
	                                	else if(shunzi_2()) res[2]++;
	                                	else if(tonghua_3()) res[3]++;
	                                	else if(zhadan_4()) res[4]++;
	                                	else if(sandaier_5()) res[5]++;
	                                	else if(liangdui_6()) res[6]++;
	                                	else if(santiao_7()) res[7]++;
	                                	else if(yidui_8()) res[8]++;
	                                	else res[9]++;
									} 
	for(int i=1;i<=9;i++)
	    printf("%d ",res[i]/6);
} 

总结分析:

做这道题我一开始也是排列组合,把自己绕来绕去,非常烧脑,所以下意识想到的办法真的真的很可能不是最优的!!!而且一般来说,我都是有意识的去避免暴力穷举。所以以后做题一定要去看看测试数据范围,看看时间限制,也多多关注一下给的空间限制,这对于我们的解题有很大帮助!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值