[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\n", ans);
88     }
89     return 0;
90 }
时间: 2024-10-11 06:21:36

[NOIp 2015]斗地主的相关文章

基础算法(搜索):NOIP 2015 斗地主

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

[BZOJ 4325][NOIP 2015] 斗地主

一道防AK好题 4325: NOIP2015 斗地主 Time Limit: 30 Sec  Memory Limit: 1024 MBSubmit: 820  Solved: 560[Submit][Status][Discuss] Description 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗地主中,牌的大小关系根据牌的数码表示如下:3<4<5<6<7<8<9<10&l

我的 NOIP 2015

2015,OI 生涯最后一个完整的赛季 从今年的Noip就已经开始了.说起来自己也是从七年级升八年级的暑假就开始接触OI了,不过和许多人一样,一直都是”混“,不知道自己到底在干什么,也没用什么功,每次noip都是考前一两个月才想起来要学OI,然后胡乱刷上几道题,就这么草草参加联赛,每次都是很虚地参加比赛,这次也是,其实到头来都是自己水平太差了,感觉好像考前也挺重视的,实际上也没什么行动,肚子里没点题目,手上怎么能熟练,上了考场紧张也是自然的,所以今年也就因为这紧张的心情Day1 也没发挥好,当然

noip 2015 day1

T1 神奇的幻方 题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间. 之后,按如下方式从小到大依次填写每个数K(K=2,3,…,N*N): 1.若(K−1)在第一行但不在最后一列,则将K填在最后一行,(K−1)所在列的右一列: 2.若(K−1)在最后一列但不在第一行,则将K填在第一列,(K−1)所在行的上一行: 3.若(K−1)在第一行最后一列,则

NOIP 2015 DAY2

跳石头 题目背景 一年一度的“跳石头”比赛又要开始了! 题目描述 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点.在起点和终点之间,有 N 块岩石(不含起点和终 点的岩石).在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达 终点. 为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳 跃距离尽可能长.由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能 移走起点和终点的岩石). 输入输出格式

noip 2015

情断斗地主,无力跳石头. ——题记(摘自llgyc) day0:第一次做动车,去年居然在初赛就挂,一直幻想着能弥补去年的遗憾..10点才到,直接洗洗睡.. day1:T1直接模拟..T2找最小环dfs即可,没注意图不联通卡了一小会.. 然后看到T3斗地主就傻了..T3看起来代码量有点大,想了好一会儿才下手..状态压缩+bfs..然后就打了不造多久才打完.. 总感觉会T,想加优先级优化下但样例还没过,代码量有点大就不写了.. 然而样例二一直过不了,出来后才发现我一直认为1.2比3小...(我怎么那

NOIP 2015 游记

本来和zly和wxh约好了 高三一起再来玩一次复赛,结果最终只有我一个人来了说...貌似是年段主任不让去...总算见识了比我们学校的YSD更爱管闲事的年段主任. 今年比赛竟然在衢州二中,学校不大,但感觉历史还是挺悠久的.报名费竟然涨了80,CCF又来骗钱了.住的酒店感觉没有以前高级,Wifi弱得只有1格信号,只能看看小说消遣了.晚上随便点了场CF的比赛打了一下,太久没碰这玩意只会Div2的水题了.然后C题一个小错误看了半个小时才看出来,E题写出来了来不及调试,只好滚去睡觉,有一种day1要滚粗的

NOIP 2015普及组复赛Day1 T1 == Codevs4510 神奇的幻方

时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题目描述 Description: 幻方是一种很神奇的N∗N矩阵:它由数字 1,2,3, … … ,N∗N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将 1写在第一行的中间.之后,按如下方式从小到大依次填写每个数(K= 2,3, … ,N∗N ): 1.若 (K−1)在第一行但不在最后一列,则将 填在最后一行,(K−1)所在列的右一列: 2.若 (K

NOIP 2015 子串

借鉴大神思路... 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<iostream> 5 #include<queue> 6 #include<stack> 7 #include<cmath> 8 #include<algorithm> 9 #include<malloc.h> 10 using names