zoukankan      html  css  js  c++  java
  • [NOIp 2015]斗地主

    Description

     牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的A到K加上大小王的共54张牌来进行的扑克牌游戏。在斗地主中,牌的大小关系根据牌的数码表示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由n张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。具体规则如下:

    Input

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

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

    Output

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

    Sample Input

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

    Sample Output

    3

    HINT

    共有1组手牌,包含8张牌:方片7,方片8,黑桃9,方片10,黑桃J,黑桃5,方

    片A以及黑桃A。可以通过打单顺子(方片7,方片8,黑桃9,方片10,黑桃J),单张
    牌(黑桃5)以及对子牌(黑桃A以及方片A)在3次内打光。
    T<=10
    N<=23

    题解

    抓取有用信息:

    出牌顺序不影响出牌次数。

    30分算法:

    1、$T≤100$,$n≤4$;
    2、先特判掉三带一的情况,然后有几种不同点数的牌,答案就是几;注意两张王可以看成是相同点数;
    3、时间复杂度$O(T*n)$

    100分算法:

    1、$T≤10$,$n≤23$;
    2、既然出牌顺序不影响,那么不妨先出对子,包括单顺、双顺、三顺。具体就是直接暴力枚举每一个顺子,然后出掉,再枚举顺子,再出掉......
    3、这样可以过吗?
    一个顺子至少有$5$张牌,最多出$4$组顺子,递归层数很小;
    然后在一组牌内可以产生$O(K^2)$个顺子,其中$K$表示能成为顺子组成部分的牌的种数,在这里$K=12$,然后这里的复杂度就是$O(K^8)$,看起来很大,其实实测完全可以跑出来;
    4、然后就可以不考虑顺子了,那么对于剩下的牌,我们就只能一个一个或者一对一对或者一带一带地出,也就是说出牌次数与牌的点数无关了;
    5、那么我们可以预处理一个$dp[a][b][c][d]$,表示手牌有"$d$张单牌,$c$个对子,$b$个三张,$a$个炸弹"的时候,把牌出完的最少次数。
    6、可以动态规划求解,$joker$可以拿出单独讨论。
    7、时间复杂度$O(n^4+T*K^8)$。

    这道题还有数据增强版,就是多考虑几个条件,把牌拆开(详见代码中的$extra$)。

     1 #include <set>
     2 #include <map>
     3 #include <ctime>
     4 #include <cmath>
     5 #include <queue>
     6 #include <stack>
     7 #include <vector>
     8 #include <cstdio>
     9 #include <string>
    10 #include <cstring>
    11 #include <cstdlib>
    12 #include <iostream>
    13 #include <algorithm>
    14 #define LL long long
    15 #define Max(a, b) ((a) > (b) ? (a) : (b))
    16 #define Min(a, b) ((a) < (b) ? (a) : (b))
    17 using namespace std;
    18 const int INF = ~0u>>1;
    19 const int lenth[4] = {0, 5, 3, 2};
    20 
    21 int n, t;
    22 int card[20], f[25][25][25][25];
    23 int ans;
    24 
    25 int getrest(int r1, int r2, int r3, int r4, int joker){
    26     if (joker == 1) r1++,joker--;
    27     if (joker) return Min(f[r4][r3][r2][r1+2], f[r4][r3][r2][r1]+1);
    28     return f[r4][r3][r2][r1];
    29 }
    30 void dfs(int t){
    31     if (t >= ans) return;
    32     int c[5] = {0};
    33     for (int i = 2; i <= 14; i++) c[card[i]]++;
    34     ans = Min(ans, t+getrest(c[1], c[2], c[3], c[4], card[0]));
    35     for (int len = 1; len <= 3; len++)
    36       for (int i = 3; i <= 14; i++){
    37           int j = i;
    38           for (;j <= 14 && card[j] >= len; j++){
    39             card[j] -= len;
    40             if (j-i+1 >= lenth[len]) dfs(t+1);
    41           }
    42           for (j--; j >= i; j--) card[j] += len;
    43       }
    44 }
    45 void pre(){
    46     memset(f, 127/3, sizeof(f));
    47     f[0][0][0][0] = 0;
    48     for (int i = 0; i <= n; i++)
    49       for (int j = 0; j <= n; j++)
    50           for (int p = 0; p <= n; p++)
    51             for (int q = 0; q <= n; q++)
    52                 if (i*4+j*3+p*2+q <= n)
    53                 {
    54                   f[i][j][p][q] = i+j+p+q;
    55                   if (i){
    56                       if (p >= 2) f[i][j][p][q] = Min(f[i][j][p][q], f[i-1][j][p-2][q]+1);//四带两对
    57                       if (q >= 2) f[i][j][p][q] = Min(f[i][j][p][q], f[i-1][j][p][q-2]+1);//四带二
    58                       if (p) f[i][j][p][q] = Min(f[i][j][p][q], f[i][j][p-1][q+2]);//extra:把对子拆成一个单的
    59                       f[i][j][p][q] = Min(f[i][j][p][q], f[i-1][j][p+2][q]);//extra:把炸拆成两对
    60                       f[i][j][p][q] = Min(f[i][j][p][q], f[i-1][j+1][p][q+1]);//extra:把炸拆成单张和三张
    61                       f[i][j][p][q] = Min(f[i][j][p][q], f[i-1][j][p][q]+1);//出炸
    62                   }
    63                   if (j){
    64                       if (p) f[i][j][p][q] = Min(f[i][j][p][q], f[i][j-1][p-1][q]+1);//三带一对
    65                       if (q) f[i][j][p][q] = Min(f[i][j][p][q], f[i][j-1][p][q-1]+1);//三带一
    66                       f[i][j][p][q] = Min(f[i][j][p][q], f[i][j-1][p+1][q+1]);//extra:三拆成二+一
    67                       f[i][j][p][q] = Min(f[i][j][p][q], f[i][j-1][p][q]+1);//直接出三张
    68                   }
    69                   if (p) f[i][j][p][q] = Min(f[i][j][p][q], f[i][j][p-1][q]+1);//直接出对子
    70                   if (q) f[i][j][p][q] = Min(f[i][j][p][q], f[i][j][p][q-1]+1);//直接出单张
    71                 }
    72 }
    73 
    74 int main(){
    75     scanf("%d%d", &t, &n);
    76     pre();
    77     while (t--){
    78       memset(card, 0, sizeof(card));
    79       int a, b;
    80       ans = n;
    81       for (int i = 1; i <= n; i++){
    82           scanf("%d%d", &a, &b);
    83           if (a == 1) card[14]++;
    84           else card[a]++;
    85       }
    86       dfs(0);
    87       printf("%d
    ", ans);
    88     }
    89     return 0;
    90 }
  • 相关阅读:
    字符编码与函数
    linux打印彩色字
    企业级docker仓库Harbor部署
    PyPI使用国内源
    CentOS 7.2 升级内核支持 Docker overlay 网络模式
    购物车2
    购物车
    定制 cobbler TITLE 信息
    06.密码错误3次锁定
    05.for循环语句
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/7471062.html
Copyright © 2011-2022 走看看