【NOIP 2015】斗地主

题目描述

牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的 A 到 K 加上大小王的共 54 张牌来进行的扑克牌游戏。在斗地主中,牌的大小关 系根据牌的数码表示如下: 3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由 n 张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。

现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。

需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。具体规则如下:

输入输出格式

输入格式:

 

第一行包含用空格隔开的2个正整数 T,n ,表示手牌的组数以及每组手牌的张数。

接下来 T 组数据,每组数据 n 行,每行一个非负数对 ai,bi ,表示一张牌,其中 ai 表示牌的数码,bi 表示牌的花色,中间用空格隔开。特别的,我们用 1 来表示数码 A , 11 表示数码 J , 12 表示数码 Q , 13 表示数码 K ;黑桃、红心、梅花、方片分别用 14 来表示;小王的表示方法为 01 ,大王的表示方法为 02 。

 

输出格式:

 

共 T 行,每行一个整数,表示打光第 i 组手牌的最少次数。

 

输入输出样例

输入样例#1:
1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1

输出样例#1:
3

输入样例#2:
1 17
12 3
4 3
2 3
5 4
10 2
3 3
12 2
0 1
1 3
10 1
6 2
12 1
11 3
5 2
12 4
2 2
7 2

输出样例#2:
6

题解

爆搜+大模拟:
有人会问:如何爆搜?
并不是真的纯爆搜,只是爆搜顺子,其余的牌贪心去打,注意顺序(代码里有解释);
此题也有一些坑,比如说‘2’不能放在顺子里(我被坑了很久,很少斗地主),这也说明学OI也是需要一些生活经验的;

code:
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<cctype>
  6 #define ll long long
  7 using namespace std;
  8 
  9 int read(){
 10     int X=0,w=0; char ch=0;
 11     while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
 12     while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
 13     return w?-X:X;
 14 }
 15 int t,n,ans,ord,x,sum;
 16 int cnt[200],a[100];
 17 //下面函数都是return 以第i张牌为首能打出顺的最大长度 
 18 int danshunzi(int i){
 19     int p=i;
 20     while(cnt[p]>=1&&p<=13)p++;
 21     if(p-i>=5)return p;
 22     return 0;
 23 }
 24 int shuangshunzi(int i){
 25     int p=i;
 26     while(cnt[p]>=2&&p<=13)p++;
 27     if(p-i>=3)return p;
 28     return 0;
 29 }
 30 int sanshunzi(int i){
 31     int p=i;
 32     while(cnt[p]>=3&&p<=13)p++;
 33     if(p-i>=2)return p;
 34     return 0;
 35 }
 36 //打完顺子以后贪心打其他牌的最小次数
 37 //第2个while和第3个while顺序不能换(虽然打出牌的数量一样) 
 38 //因为要是单独打单的话需要用两次,而打对需要用一次;
 39 //所以贪心带单 
 40 int    calc(){             
 41     int tot=0;   
 42     memset(a,0,sizeof(a));
 43     for(int i=0;i<=13;i++)a[cnt[i]]++;//桶套桶 ^-^ 
 44     while(a[4] && a[2]>1) a[4]--,a[2]-=2,tot++;// 8
 45     while(a[4] && a[1]>1) a[4]--,a[1]-=2,tot++;// 6  
 46     while(a[4] && a[2])   a[4]--,a[2]--,tot++;//  6
 47     while(a[3] && a[2])   a[3]--,a[2]--,tot++;//  5
 48     while(a[3] && a[1])   a[3]--,a[1]--,tot++;//  5
 49     return tot+a[1]+a[2]+a[3]+a[4];
 50 }
 51 
 52 void dfs(int dep){
 53     if(dep>=ans) return;
 54     int k=calc();
 55     ans=min(ans,dep+k);
 56     int pos;
 57     for(int i=2;i<=13;i++){
 58         if(sanshunzi(i)){                     //三顺子 6+
 59             pos=sanshunzi(i);
 60             for(int v=i+1;v<=pos-1;v++){      //枚举长度
 61                 for(int j=i;j<=v;j++) cnt[j]-=3;
 62                 dfs(dep+1);
 63                 for(int j=i;j<=v;j++) cnt[j]+=3;                
 64             }
 65         }        
 66     }
 67     for(int i=2;i<=13;i++){
 68         if(shuangshunzi(i)){                  //双顺子 6+
 69             pos=shuangshunzi(i);
 70             for(int v=i+2;v<=pos-1;v++){
 71                 for(int j=i;j<=v;j++) cnt[j]-=2;
 72                 dfs(dep+1);
 73                 for(int j=i;j<=v;j++) cnt[j]+=2;                
 74             }
 75         }
 76     }
 77     for(int i=2;i<=13;i++){
 78         if(danshunzi(i)){                      //单顺子 5+ 
 79             pos=danshunzi(i);
 80             for(int v=i+4;v<=pos-1;v++){
 81                 for(int j=i;j<=v;j++) cnt[j]--;
 82                 dfs(dep+1);
 83                 for(int j=i;j<=v;j++) cnt[j]++;                
 84             }
 85         }
 86     }
 87 }
 88 
 89 int main()
 90 {
 91     freopen("cards.in","r",stdin);
 92     freopen("cards.out","w",stdout);
 93     t=read();n=read();
 94     while(t--){
 95         ans=30;
 96         memset(cnt,0,sizeof(cnt));
 97         for(int i=1;i<=n;i++){
 98             x=read();ord=read();
 99             if(x==1)x=13;      // A 
100             else if(x) x--;    // 1对应2 2对应3 ....... 
101             cnt[x]++;          // 这样存可以解决2不能在顺子里的坑 
102         }
103         dfs(0);
104         printf("%d\n",ans);
105     }
106     return 0;
107 }
我觉得我该斗两把地主了 ^-^

 



转载于:https://www.cnblogs.com/ZAGER/p/9445839.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值