zoukankan      html  css  js  c++  java
  • 【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
    ",ans);
    105     }
    106     return 0;
    107 }
    我觉得我该斗两把地主了 ^-^


  • 相关阅读:
    Python脚本抓取京东手机的配置信息
    Python中的Pandas模块
    Python中的Pandas模块
    XML和JSON数据格式
    XML和JSON数据格式
    Linux运维比较常用的一些脚本
    Linux运维比较常用的一些脚本
    Keepalived高可用集群
    Keepalived高可用集群
    Linux中正则表达式和字符串的查询、替换(tr/diff/wc/find)
  • 原文地址:https://www.cnblogs.com/ZAGER/p/9445839.html
Copyright © 2011-2022 走看看