zoukankan      html  css  js  c++  java
  • UOJ147 斗地主

    题目描述

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

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

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

    牌型牌型说明牌型举例
    火箭 即双王(双鬼牌) ♂ ♀
    炸弹 四张同点牌。 ♠A ♥A ♣A ♦A
    单张牌 单张牌 ♠3
    对子牌 两张码数相同的牌 ♠2 ♥2
    三张牌 三张码数相同的牌 ♠3 ♥3 ♣3
    三带一 三张码数相同的牌 + 一张单牌 ♠3 ♥3 ♣3 ♠4
    三带二 三张码数相同的牌 + 一对牌 ♠3 ♥3 ♣3 ♠4 ♥4
    单顺子 五张或更多码数连续的单牌(不包括 2 点和双王) ♠7 ♣8 ♠9 ♣10 ♣J
    双顺子 三对或更多码数连续的对牌(不包括 2 点和双王) ♣3 ♥3 ♠4 ♥4 ♠5 ♥5
    三顺子 二个或更多码数连续的三张牌(不能包括 2 点和双王) ♠3 ♥3 ♣3 ♠4 ♥4 ♣4 ♠5 ♦5 ♥5
    四带二 四张码数相同的牌+任意两张单牌(或任意两对牌) ♠5 ♥5 ♣5 ♦5 ♣3 ♣8

    输入格式

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

    接下来 TT 组数据,每组数据 nn 行,每行一个非负整数对 ai,biai,bi ,表示一张牌,其中 aiai 表示牌的数码, bibi 表示牌的花色,中间用空格隔开。特别的,我们用 11 来表示数码 A, 1111表示数码 J, 1212 表示数码 Q, 1313 表示数码 K;黑桃、红心、梅花、方片分别用 1-4 来表示;小王的表示方法为 0 1 ,大王的表示方法为 0 2 。

    输出格式

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

    样例一

    input

    1 8
    7 4
    8 4
    9 1
    10 4
    11 1
    5 1
    1 4
    1 1
    
    

    output

    3
    
    

    explanation

    共有 11 组手牌,包含 88 张牌:方片 7,方片 8,黑桃 9,方片 10,黑桃 J,黑桃 5,方片 A以及黑桃 A。可以通过打单顺子(方片 7,方片 8,黑桃 9,方片 10,黑桃 J),单张牌(黑桃 5)以及对子牌(黑桃 A以及方片 A)在 33 次内打光。

    样例二

    input

    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
    
    

    output

    6
    
    

    数据规模与约定

    对于不同的测试点,我们约定手牌组数 TT ,与张数 nn 的规模如下:

    测试点编号TT 的规模nn 的规模测试点编号TT 的规模nn 的规模
    1 100100 22 11 100100 1414
    2 100100 22 12 100100 1515
    3 100100 33 13 1010 1616
    4 100100 33 14 1010 1717
    5 100100 44 15 1010 1818
    6 100100 44 16 1010 1919
    7 100100 1010 17 1010 2020
    8 100100 1111 18 1010 2121
    9 100100 1212 19 1010 2222
    10 100100 1313 20 1010 2323

    数据保证:所有的手牌都是随机生成的。

     

     

    正解:搜索

    解题报告:

      这道题做法很多,可以状压,也可以搜索+剪枝。

      我的做法的话就是搜索+最优性剪枝,每次对于当前局面得到一个答案上界,就是能带就带,然后我只需要枚举每次打了什么顺子(三顺子、双顺子、单顺子)就可以了。

     

     1 //It is made by jump~
     2 #include <iostream>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <cstdio>
     6 #include <cmath>
     7 #include <algorithm>
     8 #include <ctime>
     9 #include <vector>
    10 #include <queue>
    11 #include <map>
    12 #include <set>
    13 using namespace std;
    14 typedef long long LL;
    15 #define RG register
    16 const int inf = (1<<30);
    17 int n,ans;
    18 int a[15],cnt[5];
    19 
    20 inline int getint()
    21 {
    22     int w=0,q=0; char c=getchar();
    23     while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 
    24     while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w;
    25 }
    26 
    27 inline int suan(){
    28     for(int i=1;i<=4;i++) cnt[i]=0; for(int i=0;i<=13;i++) cnt[a[i]]++;
    29     int tot=0;
    30     while(cnt[4]>0 && cnt[2]>=2) tot++,cnt[4]--,cnt[2]-=2;//计算四带二
    31     while(cnt[4]>0 && cnt[1]>=2) tot++,cnt[4]--,cnt[1]-=2;//计算四带一
    32     while(cnt[3]>0 && cnt[2]>0) tot++,cnt[3]--,cnt[2]--;//计算三带二
    33     while(cnt[3]>0 && cnt[1]>0) tot++,cnt[3]--,cnt[1]--;//计算三带一
    34     return tot+cnt[4]+cnt[3]+cnt[2]+cnt[1];
    35 } 
    36 
    37 inline void dfs(int step){//每次尽可能消耗的牌多
    38     if(step>=ans) return ;
    39     ans=min(ans,step+suan());
    40     int now;
    41     for(int i=2;i<=13;i++) {//三顺子
    42     now=14;    for(int j=i;j<=13;j++) if(a[j]<3) { now=j; break; } 
    43     if(now-i>=2) {
    44         for(int k=now-i;k>=2;k--) {
    45         for(int l=i;l<i+k;l++) a[l]-=3;
    46         dfs(step+1);
    47         for(int l=i;l<i+k;l++) a[l]+=3;
    48         }
    49     }
    50     }
    51     for(int i=2;i<=13;i++) {//双顺子
    52     now=14; for(int j=i;j<=13;j++) if(a[j]<2) { now=j; break; }
    53     if(now-i>=3) {
    54         for(int k=now-i;k>=3;k--) {
    55         for(int l=i;l<i+k;l++) a[l]-=2;
    56         dfs(step+1);
    57         for(int l=i;l<i+k;l++) a[l]+=2;
    58         }
    59     }
    60     }
    61     for(int i=2;i<=13;i++) {//单顺子
    62     now=14; for(int j=i;j<=13;j++) if(a[j]<1) { now=j; break; }
    63     if(now-i>=5) {
    64         for(int k=now-i;k>=5;k--) {
    65         for(int l=i;l<i+k;l++) a[l]--;
    66         dfs(step+1);
    67         for(int l=i;l<i+k;l++) a[l]++;
    68         }
    69     }
    70     }
    71 }
    72 
    73 inline void work(){
    74     int T=getint(); n=getint(); int x;
    75     while(T--) {
    76     memset(a,0,sizeof(a));
    77     for(int i=1;i<=n;i++) {
    78         x=getint(); 
    79         if(x==1) a[13]++;
    80         else if(x==0) a[0]++;
    81         else a[x-1]++;
    82         x=getint();
    83     }
    84     ans=suan();dfs(0);
    85     printf("%d
    ",ans);
    86     }
    87 }
    88 
    89 int main()
    90 {
    91     work();
    92     return 0;
    93 }
  • 相关阅读:
    如何用消息系统避免分布式事务?
    jvm调休,监控
    ClassLoader原理
    JVM结构、GC工作机制详解
    单链表倒置
    hashSet
    HashMap
    hashcode
    深入理解HTTP协议、HTTP协议原理分析
    HTTP协议(详解一)
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/5939165.html
Copyright © 2011-2022 走看看