【P2668】斗地主【NOIP2015】

2023-12-14 05:30
文章标签 斗地主 noip2015 p2668

本文主要是介绍【P2668】斗地主【NOIP2015】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

链接:https://www.luogu.org/problemnew/show/P2668

题目描述

牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的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;黑桃、红心、梅花、方片分别用 1−4 来表示;小王的表示方法为 0 1 ,大王的表示方法为 0 2 。

输出格式:

共 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

说明

样例1说明

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

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

在这里插入图片描述

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

思路

弱弱的说一句俺斗地主玩的超级差QAQ

  • 这道题的核心解法在于搜索(外加一点点的贪心)

  • 由题目分析可知,花色这东西是没啥用的,所以直接不管

  • 并且容易发现,出牌的顺序对于这道题没有任何影响

因此,我们应该先把顺子打出来(这样我们可以快速+轻松+愉快的打出五张以上的牌),剩下的就用贪心,能多带牌就多带牌。
顺子的话,直接暴力,先搜三顺子,到双顺子,再单顺子(贪心),在带牌的时候从四带二开始一个个枚举,差不多就是这样。

注意
  • 统计顺子的时候不能将大小鬼2统计进去!
  • 四带二可以带一对,两单张,两对
  • 注意细节!

Code

//斗地主(搜索)
#include<bits/stdc++.h>
using namespace std;
int card[20],n,T,ans;
inline int read(){int num=0;char ch;ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch<='9'&&ch>='0'){num=num*10+ch-'0';ch=getchar();}return num;
}
void init(){int a,b;memset(card,0,sizeof(card));for(int i=1;i<=n;i++){a=read(),b=read();if(a>=3&&a<=13)card[a-2]++;if(a==1)card[12]++;if(a==2)card[13]++;if(a==0)card[14]++;}ans=n;
}
inline int SP(){int cnt[5]={0},temp[18]={0},res=0;for(int i=1;i<=14;i++)cnt[card[i]]++;for(int i=1;i<=14;i++)temp[i]=card[i];if(cnt[4]&&(cnt[3]>2||cnt[2]>2)){//四带二对 for(int i=1;i<=14;i++){if(temp[i]==4){for(int j=1;j<=14;j++){if((j==i)||(temp[j]!=2))continue;if(temp[i]!=4)break;for(int k=1;k<=14;k++){if(k==i||k==j)continue;if(temp[k]==2){temp[i]-=4,temp[j]-=2,temp[k]-=2;res++;cnt[4]--;break;}}}}}}if(cnt[4]&&(cnt[3]||cnt[2]||(cnt[1]))){//四带二单 for(int i=1;i<=14;i++){if(temp[i]==4){for(int j=1;j<=14;j++){if((i==j)||temp[j]!=1)continue;if(temp[i]!=4)break;for(int k=1;k<=14;k++){if((i==k)||(j==k))continue;if(temp[k]==1){temp[i]-=4,temp[j]--,temp[k]--;res++;break;}}}}}}if(cnt[4]&&(cnt[3]||cnt[2])){//四带一对 for(int i=1;i<=14;i++){if(temp[i]==4){for(int j=1;j<=14;j++){if(j==i)continue;if(temp[j]==2){temp[i]-=4,temp[j]-=2,res++;cnt[4]--;break;}}}} }for(int i=1;i<=14;i++){//三带二、一if(temp[i]>=3){for(int j=1;j<=14;j++){if(i==j)continue;if(temp[j]==2){temp[i]-=3,temp[j]-=2,res++;break;}if(temp[j]==1){temp[i]-=3,temp[j]--,res++;break;}}}}for(int i=1;i<=14;i++){//散牌 if(temp[i]==4)temp[i]-=4,res++;else if(temp[i]==3)temp[i]-=3,res++;else if(temp[i]==2)temp[i]-=2,res++;else if(temp[i]==1)temp[i]--,res++;}return res;
}
void DFS(int deep){if(deep>=ans)return;for(int i=1;i<=14;i++){if(card[i])break;if(i==14){ans=deep;return;}}int temp=SP();if(temp+deep<ans)ans=temp+deep;int cnt[5]={0};for(int i=1;i<=14;i++)cnt[card[i]]++;if(cnt[3]>=2){//三顺子 for(int i=1;i<=10;i++){for(int l=2;l<=12;l++){for(int p=i;p<=l+i;p++){if((card[p]<3)||(p>12))break;else{if(p==l+i){for(int q=p-l;q<=p;q++)card[q]-=3;cnt[3]-=l+1; DFS(deep+1); cnt[3]+=l+1;for(int q=p-l;q<=p;q++)card[q]+=3;}}}}}}if(cnt[2]>=3){//双顺子 for(int i=1;i<=10;i++){for(int l=2;l<=12;l++){for(int p=i;p<=l+i;p++){if((card[p]<2)||(p>12))break;else{if(p==l+i){for(int q=p-l;q<=p;q++)card[q]-=2;cnt[2]-=l+1; DFS(deep+1); cnt[2]+=l+1;for(int q=p-l;q<=p;q++)card[q]+=2;}}}}}}if(cnt[1]+cnt[2]+cnt[3]>5){//单顺子 for(int i=1;i<=10;i++){for(int l=4;l<=12;l++){for(int p=i;p<=i+l;p++){if((!card[p])||(p>12))break;else{if(p==l+i){for(int q=p-l;q<=p;q++)card[q]--;DFS(deep+1);for(int q=p-l;q<=p;q++)card[q]++;}}}}}}
}
int main(){T=read(),n=read();while(T--){init();DFS(0);printf("%d\n",ans);}return 0;
} 

END

PS 说实话俺第一遍没做出来这道题QAQ是因为我斗地主太弱的原因吗15551

这篇关于【P2668】斗地主【NOIP2015】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/491288

相关文章

信息学奥赛初赛天天练-80-NOIP2015普及组-基础题5-错位排列、二叉树、完全二叉树、叶子节点、完全二叉树叶子节点

NOIP 2015 普及组 基础题5 21 重新排列 1234使得每一个数字都不在原来的位置上,一共有( )种排法 22 一棵结点数为 2015的二叉树最多有( )个叶子结点 2 相关知识点 1) 错位排列 考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n) 错排问题最早被尼古拉·伯努利和欧拉研究

信息学奥赛初赛天天练-79-NOIP2015普及组-基础题4-即时通讯软件、二叉树遍历、前序遍历、中序遍历、后序遍历、算法时间复杂度

NOIP 2015 普及组 基础题4 11 下面哪种软件不属于即时通信软件( ) A QQ B MSN C 微信 D P2P 16 前序遍历序列与中序遍历序列相同的二叉树为( ) A 根结点无左子树 B 根结点无右子树 C 只有根结点的二叉树或非叶子结点只有左子树的二叉树 D 只有根结点的二叉树或非叶子结点只有右子树的二叉树 18 下列选项中不属于视频文件格式的是( ) A TXT B AV

信息学奥赛初赛天天练-76-NOIP2015普及组-基础题1-计算机存储、硬件系统、操作系统、进制转换、二进制加法

NOIP 2016 普及组 基础题1 1 1MB 等于 ( ) A 10000 字节 B 1024 字节 C 1000×1000 字节 D 1024×1024 字节 2 在 PC 机中,PENTIUM(奔腾)、酷睿、赛扬等 是指( ) A 生产厂家名称 B 硬盘的型号 C CPU 的型号 D 显示器的型号 3 操作系统的作用是( ) A 把源程序译成目标程序 B 便于进行数据管理 C 控制和

JAVA编程思想:斗地主扑克牌

斗地主大家都玩过,扑克牌一共54张牌,底牌三张剩下的51张正好是每人17张。 我们需要干的事情就是: 1. 先获取54张牌, 2. 把它打乱, 3. 抽出去三张底牌, 4. 将51张平均分给三个人 我们之前学到过多种集合,list,set,map,实现这个我们应该选择哪种数据结构呢? 答案是:MAP 为什么是MAP我们来分析一下,我们都知道斗地主发牌之后,我们到手里面的牌最好是有序的,方

java排序查找算法,Map集合,集合嵌套以及斗地主案例的实现

排序查找算法,Map集合,集合嵌套,斗地主案例 主要内容 : TreeSet集合(重点) 排序算法(理解) 查找算法(理解) Map集合(重点) 集合嵌套(理解) 斗地主案例(理解) 1 TreeSet集合 1.1 集合体系 Collection List接口 ArrayList类LinkedList类 Set接口 HashSet集合 TreeSet集合

Java 写一个可以给斗地主玩家随机发牌的程序。

写一个可以给斗地主玩家随机发牌的程序。a:牌可以随机发给三个玩家b:在控制台打印每个玩家的牌。c:对每个玩家手中的牌按照大小排序。 思路:创建一个容器存储所有的牌,再创建三个容器分别表示三个用户的牌,三个用户轮流从第一个容器中取牌,剩下三张为底牌。把大王和小王也算进去。可以封装一个类表示牌 package hcq.hw; imp

简单用java集合模拟斗地主发牌操作

简易斗地主发牌(熟悉java集合的使用) 1、需求 按照斗地主规则,完成洗牌发牌的动作。 具体要求如下: 1、准备牌:组装54张扑克牌 2、洗牌:54张牌顺序打乱 3、发牌:三个玩家参与游戏,三个人交替摸牌,每人17张,最后三张留作底牌 4、看牌:查看三人各自手中的牌(按照牌的大小排序)、底牌 规则:手中扑克牌从大到小的摆放顺序:大王、小王、2、A、K、Q、J、10、9、8、7、6

重学java 52.集合 斗地主案例

你太锐利了,这些年来,风霜雨雪,踉跄清冷,我相信你所有的苦楚                                                                                                 —— 24.5.30 1 案例介绍 按照斗地主的规则,完成洗牌发牌的动作。 具体规则:         使用54张牌打乱顺序,三个玩

JavaSE——集合框架二(2/6)-综合案例-斗地主游戏(做牌、洗牌、发牌、排序、看牌)

目录 需求与分析 具体实现 牌类定义 房间类定义 初步测试  启动游戏 运行案例 需求与分析 需求 总共有54张牌点数:"3","4","5","6","7","8","9","10","J","Q","K","A","2"花色:"♠","♥","♣","♦"大小王:"👲","🃏"斗地主:发出51张牌,剩下3张作为底牌 分析实现 在启动游戏房间的时候,应该提

Java控制台实现斗地主的洗牌和发牌功能

一、题目要求     有3个玩家:A,B,C。底牌有三张牌,每个人共17张牌,共(17*3+3=54)张牌,实现洗牌与发牌,只在控制没有实现UI可视化。 二、思路 1、用List集合存储所有的扑克牌。 2、洗牌:随机打乱牌的顺序,用到Collections工具类。 public static void shuffle(List<?> list) 使用默认的随机源随机排列指定的列表