牌型种数|2015年蓝桥杯B组题解析第七题-fishers

牌型种数

小明被劫持到X赌城,被迫与其他3人玩牌。
一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
这时,小明脑子里突然冒出一个问题:
如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序,自己手里能拿到的初始牌型组合一共有多少种呢?

请填写该整数,不要填写任何多余的内容或说明文字。

答案:3598180

思路一:暴力法,12层循环枚举取得各个牌得数量(想不到其它方法,这种方法最有效!)

思路二:递归求组合数,从52张牌中选出13张,

代码一:暴力枚举

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
 int a[13]; 
 static int count;
 int ans = 0;
 for(a[0]=0; a[0]<=4; a[0]++)
 {
  for(a[1]=0; a[1]<=4; a[1]++)
  {
   for(a[2]=0; a[2]<=4; a[2]++)
   {
    for(a[3]=0; a[3]<=4; a[3]++)
    {
     for(a[4]=0; a[4]<=4; a[4]++)
     {
      for(a[5]=0; a[5]<=4; a[5]++)
      {
       for(a[6]=0; a[6]<=4; a[6]++)
       { 
        for(a[7]=0; a[7]<=4; a[7]++)
        {
         for(a[8]=0; a[8]<=4; a[8]++)
         {
          for(a[9]=0; a[9]<=4; a[9]++)
          {
           for(a[10]=0; a[10]<=4; a[10]++)
           {
            for(a[11]=0; a[11]<=4; a[11]++)
            {
             for(a[12]=0; a[12]<=4; a[12]++)
             {
              if(a[0]+a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]+a[9]+a[10]+a[11]+a[12]==13)
              {
               count++;
               ans = count;
              }
             }
            }
           }
          }
         }
        }
       }
      } 
     }
    }
   }
  }
 }
 cout<<ans<<endl;
 return 0;
}

代码二:dfs,递归求组合数,

#include<iostream>  
#include<algorithm>  
#include<cmath>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
int sum=0;  

//两个参数:pos表示牌的号码1~13 cnt表示当前选择的是第几张牌
//从第1号牌 开始选第一张 
void dfs(int pos, int cnt){  
	//选够了13张牌 sum总数加1 
    if(cnt==14){  
        sum++;  
        return;  
    }
    //选到第14号牌了 还没有选够13张 return 
    if(pos==14){  
        return;  
    }  
    int num=min(14-cnt,4);//min取两者中的小值   这句话剪枝! 
    
    for(int i=0;i<=num;i++){  
        dfs(pos+1,cnt+i); //cnt+i表示当前手里已经选好的牌的数目 
    }  
    return;  
}  
int main(){  
    dfs(1,1); //增加参数 2个参数:从第1号牌 开始选第一张 不选、选1张...选4张 
    printf("%d\n",sum);  
    return 0;  
}  

代码三:dfs回溯,(超时)。

#include<iostream>
using namespace std;

//回溯法:按字典序选择,但是会超时! 

int have[20];
int sum = 0;
int a[15];


void init(){
	for(int i=1;i<=14;i++){
		have[i] = 0;
	}
}


//下面是错误写法:时间复杂度太高 n^n 这里是5^13次方(10亿)
//递归程序中不能有太多循环! 
void dfs(int k){
	if(k==5){
		//判断是否有这个组合 
		sum++;
		for(int j=1;j<14;j++){
			cout<<a[j]<<" ";
		}
		cout<<endl;
		return;
	}
	//循环13次 时间复杂度太高 
	for(int i=1;i<=13;i++){
		if(have[i] < 4){
			have[i]++; 
			a[k] = i; 
			dfs(k+1); 
			have[i]--;
		}
	} 
}

int main(){
	init();
	dfs(1);
	cout<<sum<<endl;
} 

/这道题目给我的思考是,
在用暴力法(dfs递归)的时候直接多想几种可能,
多想几种方案(这里选04张牌,而不是用回溯法从113张中选)
这样可以很大程度上节约时间
/

posted @ 2019-01-21 18:12  fishers  阅读(711)  评论(0编辑  收藏  举报