【题目描述】

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

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

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

具体规则如下:

本题数据随机,不支持hack,要hack或强力数据请点击这里

输入输出格式

输入格式:

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

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

输出格式:

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

输入输出样例

输入样例#1:

  1. 1 8
  2. 7 4
  3. 8 4
  4. 9 1
  5. 10 4
  6. 11 1
  7. 5 1
  8. 1 4
  9. 1 1
输出样例#1:

  1. 3
输入样例#2:

  1. 1 17
  2. 12 3
  3. 4 3
  4. 2 3
  5. 5 4
  6. 10 2
  7. 3 3
  8. 12 2
  9. 0 1
  10. 1 3
  11. 10 1
  12. 6 2
  13. 12 1
  14. 11 3
  15. 5 2
  16. 12 4
  17. 2 2
  18. 7 2
输出样例#2:

  1. 6

说明

样例1说明

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

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

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

自测时果断打了个状压dp,其中f[i]表示当前手上牌的状态为i时需要的最少出牌次数(i的第k个二进制位表示排序后的第k张牌是否出手)。f[i]的转移比较复杂,因此代码打了200+行.....

时间复杂度为O(4*3^7),但由于f[i]转移条件判断较为耗时,转移方法较多,常数巨大!!

结果这个状压代码只拿了80分...最后四个点TLE(如果不是多组数据就A了.....)

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #define M 18
  6. #define N 14
  7. #define INF 123123123
  8. using namespace std;
  9. int n,st,f[<<]={},cnt[M]={},a[M]={},l[M]={},r[M]={};
  10.  
  11. bool hf(int x){
  12. bool wei1,wei2;
  13. for(int i=;i<n;i++){
  14. wei1=x&(<<i); wei2=x&(<<(i-));
  15. if(a[i]==a[i-]&&wei2&&(!wei1)) return ;
  16. }
  17. return ;
  18. }
  19. int dan(int y,int x){
  20. if(l[x]==-) return st;
  21. int k=(<<(r[x]-l[x]+))-;
  22. int delta=(y>>l[x])&k;
  23. if(delta){
  24. delta=(delta<<)&k;
  25. y=(y&((<<l[x])-))|(delta<<l[x])|((y>>(r[x]+))<<(r[x]+));
  26. return y;
  27. }
  28. return st;
  29. }
  30. int dui(int y,int x){
  31. if(l[x]==-) return st;
  32. int k=(<<(r[x]-l[x]+))-;
  33. int delta=(y>>l[x])&k;
  34. if((delta<<)&k){
  35. delta=(delta<<)&k;
  36. y=(y&((<<l[x])-))|(delta<<l[x])|((y>>(r[x]+))<<(r[x]+));
  37. return y;
  38. }
  39. return st;
  40. }
  41. int san(int y,int x){
  42. if(l[x]==-) return st;
  43. int k=(<<(r[x]-l[x]+))-;
  44. int delta=(y>>l[x])&k;
  45. if((delta<<)&k){
  46. delta=(delta<<)&k;
  47. y=(y&((<<l[x])-))|(delta<<l[x])|((y>>(r[x]+))<<(r[x]+));
  48. return y;
  49. }
  50. return st;
  51. }
  52. int zha(int y,int x){
  53. if(l[x]==-) return st;
  54. int k=(<<(r[x]-l[x]+))-;
  55. int delta=(y>>l[x])&k;
  56. if((delta<<)&k){
  57. delta=(delta<<)&k;
  58. y=(y&((<<l[x])-))|(delta<<l[x])|((y>>(r[x]+))<<(r[x]+));
  59. return y;
  60. }
  61. return st;
  62. }
  63. int sandaiyi(int y,int x,int dai){
  64. y=san(y,x);
  65. if(y==st) return st;
  66. return dan(y,dai);
  67. }
  68. int sandaier(int y,int x,int dai){
  69. y=san(y,x);
  70. if(y==st) return st;
  71. return dui(y,dai);
  72. }
  73. int sidaier(int y,int x,int dai1,int dai2){
  74. y=zha(y,x);
  75. if(y==st) return st;
  76. y=dan(y,dai1);
  77. if(y==st) return st;
  78. return dan(y,dai2);
  79. }
  80. int sidaidui(int y,int x,int dai1,int dai2){
  81. y=zha(y,x);
  82. if(y==st) return st;
  83. y=dui(y,dai1);
  84. if(y==st) return st;
  85. return dui(y,dai2);
  86. }
  87. int shunzi(int y,int ll,int rr){
  88. for(int i=ll;i<=rr;i++){
  89. y=dan(y,i);
  90. if(y==st) return st;
  91. }
  92. return y;
  93. }
  94. int liandui(int y,int ll,int rr){
  95. for(int i=ll;i<=rr;i++){
  96. y=dui(y,i);
  97. if(y==st) return st;
  98. }
  99. return y;
  100. }
  101. int sanshun(int y,int ll,int rr){
  102. for(int i=ll;i<=rr;i++){
  103. y=san(y,i);
  104. if(y==st) return st;
  105. }
  106. return y;
  107. }
  108. int Main(){
  109. memset(cnt,,sizeof(cnt));
  110. memset(a,,sizeof(a));
  111. memset(l,-,sizeof(l)); memset(r,-,sizeof(r));
  112. for(int i=;i<=n;i++){
  113. int x,k; scanf("%d%d",&x,&k);
  114. if(x<){
  115. if(x==) x=;
  116. else if(x==) x=;
  117. else x=;
  118. }
  119. x=+(x-+N)%N;
  120. cnt[x]++;
  121. }
  122. for(int i=,j=;i<=N;i++){
  123. bool ck=;
  124. while(cnt[i]){
  125. cnt[i]--;
  126. if(ck){l[i]=j; ck=;}
  127. a[j++]=i;
  128. }
  129. if(!ck) r[i]=j-;
  130. }
  131. st=(<<n)-;
  132. for(int i=st;i>=;i--) f[i]=INF;
  133. f[st]=;
  134. for(int i=st;i;i--) if(hf(i)){
  135. if(i==){
  136. f[st]=;
  137. }
  138. for(int j=;j<=N;j++){
  139. int y=dan(i,j);
  140. if(y!=st) f[y]=min(f[y],f[i]+);
  141. }
  142. for(int j=;j<=N;j++){
  143. int y=dui(i,j);
  144. if(y!=st) f[y]=min(f[y],f[i]+);
  145. }
  146. for(int j=;j<=N;j++){
  147. int y=san(i,j);
  148. if(y!=st) f[y]=min(f[y],f[i]+);
  149. }
  150. for(int j=;j<N;j++){
  151. int y=zha(i,j);
  152. if(y!=st) f[y]=min(f[y],f[i]+);
  153. }
  154. for(int j=;j<=N;j++) if(l[j]!=-&&(r[j]-l[j]>)){
  155. for(int k=;k<=N;k++) if(l[k]!=-){
  156. int y=sandaiyi(i,j,k);
  157. if(y!=st) f[y]=min(f[y],f[i]+);
  158. }
  159. }
  160. for(int j=;j<=N;j++) if(l[j]!=-&&(r[j]-l[j]>)){
  161. for(int k=;k<=N;k++) if(l[k]!=-){
  162. int y=sandaier(i,j,k);
  163. if(y!=st) f[y]=min(f[y],f[i]+);
  164. }
  165. }
  166. for(int j=;j<N;j++) if(l[j]!=-){
  167. for(int k=j+;k<=N-;k++) if(l[k]!=-){
  168. int y=shunzi(i,j,k);
  169. if(y!=st) f[y]=min(f[y],f[i]+);
  170. }
  171. }
  172. for(int j=;j<N;j++) if(l[j]!=-&&(r[j]-l[j]>)){
  173. for(int k=j+;k<=N-;k++) if(l[k]!=-&&(r[k]-l[k]>)){
  174. int y=liandui(i,j,k);
  175. if(y!=st) f[y]=min(f[y],f[i]+);
  176. }
  177. }
  178. for(int j=;j<N;j++) if(l[j]!=-&&(r[j]-l[j]>)){
  179. for(int k=j+;k<=N-;k++) if(l[k]!=-&&(r[k]-l[k]>)){
  180. int y=sanshun(y,j,k);
  181. if(y!=st) f[y]=min(f[y],f[i]+);
  182. }
  183. }
  184. for(int j=;j<=N;j++) if(l[j]!=-&&(r[j]-l[j]>)){
  185. for(int k=;k<=N;k++) if(l[k]!=-){
  186. for(int L=;L<=N;L++) if(l[L]!=-){
  187. int y=sidaier(i,j,k,L);
  188. if(y!=st) f[y]=min(f[y],f[i]+);
  189. }
  190. }
  191. }
  192. for(int j=;j<=N;j++) if(l[j]!=-&&(r[j]-l[j]>)){
  193. for(int k=;k<=N;k++) if(l[k]!=-){
  194. for(int L=;L<=N;L++) if(l[L]!=-){
  195. int y=sidaidui(i,j,k,L);
  196. if(y!=st) f[y]=min(f[y],f[i]+);
  197. }
  198. }
  199. }
  200. }
  201. printf("%d\n",f[]);
  202. }
  203.  
  204. int main(){
  205. freopen("landlords.in","r",stdin);
  206. freopen("landlords.out","w",stdout);
  207. int cas; scanf("%d%d",&cas,&n);
  208. while(cas--) Main();
  209. }

事实证明我想多了...

直接上爆搜,暴力枚举单顺,双顺和三顺,剩下的牌直接贪心(即按四带二,四带一对,四代两对,三带一,三代二,炸弹,三条,对子,单牌的顺序出,不难看出这么出牌是最优的,即出牌次数取决于顺的选择方式)。

时间复杂度O(玄学) ,所有点本地0.01s内跑出

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #define M 100
  5. #define N 14
  6. using namespace std;
  7. int n,ans=;
  8. int a[M]={},c[M]={};
  9.  
  10. int get(){
  11. c[]=c[]=c[]=c[]=c[]=;
  12. for(int i=;i<=N;i++) c[a[i]]++;
  13. int sum=;
  14. while(c[]&&c[]>=) c[]--,c[]-=,sum++;
  15. while(c[]&&c[]>=) c[]--,c[]-=,sum++;
  16. while(c[]&&c[]) c[]--,c[]--,sum++;
  17. while(c[]&&c[]) c[]--,c[]--,sum++;
  18. while(c[]&&c[]) c[]--,c[]--,sum++;
  19. return sum+c[]+c[]+c[]+c[];
  20. }
  21.  
  22. void dfs(int x){
  23. if(x>=ans) return;
  24. ans=min(ans,x+get());
  25. for(int i=;i<=;i++){
  26. if(a[i]==||a[i+]==||a[i+]==||a[i+]==) continue;
  27. for(int j=i+;j<=;j++) if(a[j]==) break;
  28. else{
  29. for(int k=i;k<=j;k++) a[k]--;
  30. dfs(x+);
  31. for(int k=i;k<=j;k++) a[k]++;
  32. }
  33. }
  34. for(int i=;i<=;i++){
  35. if(a[i]<||a[i+]<) continue;
  36. for(int j=i+;j<=;j++) if(a[j]<) break;
  37. else{
  38. for(int k=i;k<=j;k++) a[k]-=;
  39. dfs(x+);
  40. for(int k=i;k<=j;k++) a[k]+=;
  41. }
  42. }
  43. for(int i=;i<=;i++){
  44. if(a[i]<) continue;
  45. for(int j=i+;j<=;j++) if(a[j]<) break;
  46. else{
  47. for(int k=i;k<=j;k++) a[k]-=;
  48. dfs(x+);
  49. for(int k=i;k<=j;k++) a[k]+=;
  50. }
  51. }
  52. }
  53.  
  54. int main(){
  55. freopen("landlords.in","r",stdin);
  56. freopen("landlords.out","w",stdout);
  57. int cas; cin>>cas>>n;
  58. while(cas--){
  59. memset(a,,sizeof(a));
  60. for(int i=;i<=n;i++){
  61. int x,k; scanf("%d%d",&x,&k);
  62. if(x<){if(x==) x=; else if(x==) x=; else x=;}
  63. x=+(x-+N)%N; a[x]++;
  64. }
  65. ans=; dfs();
  66. printf("%d\n",ans);
  67. }
  68. }

【NOIP2015提高组】 Day1 T3 斗地主的更多相关文章

  1. NOIP2015 提高组 Day T3 斗地主

    题目描述 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共5张牌来进行的扑克牌游戏.在斗地主中,牌的大小关 系根据牌的数码表示如下:3<4< ...

  2. Noip2011 提高组 Day1 T3 Mayan游戏

    题目描述 Mayan puzzle是最近流行起来的一个游戏.游戏界面是一个 7 行5 列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上.游戏通关是指在规定 ...

  3. Noip2015 提高组 Day1

    T1神奇的幻方 直通 思路: 制定一个lrow记录上一个数字所在的行数,lcolume记录上一个数字所在的列数,然后根据题目的描述进行更改即可 上代码: #include <iostream&g ...

  4. noip2015 提高组day1、day2

    NOIP201505神奇的幻方   试题描述 幻方是一种很神奇的N∗N矩阵:它由数字 1,2,3,……,N∗N构成,且每行.每列及两条对角线上的数字之和都相同.    当N为奇数时,我们可以通过以下方 ...

  5. 2012Noip提高组Day1 T3 开车旅行

    题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的 城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 Hi,城市 ...

  6. NOIP2011提高组 Day1 T3 Mayan游戏

    题目描述 Mayan puzzle是最近流行起来的一个游戏.游戏界面是一个7行×5列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上.游戏通关是指在规定的步 ...

  7. NOIP2015提高组Day1 Message

    题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一 ...

  8. 洛谷 3953 NOIP2017提高组Day1 T3 逛公园

    [题解] 先建反向图,用dijkstra跑出每个点到n的最短距离dis[i] 设f[u][k]表示dis(u,n)<=mindis(u,n)+k的方案数.对于边e(u,v,w),走了这条边的话需 ...

  9. 【题解】NOIP2015提高组 复赛

    [题解]NOIP2015提高组 复赛 传送门: 神奇的幻方 \([P2615]\) 信息传递 \([P2661]\) 斗地主 \([P2668]\) 跳石头 \([P2678]\) 子串 \([P26 ...

  10. 2015 Noip提高组 Day1

    P2615 神奇的幻方 [题目描述] 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: ...

随机推荐

  1. ASCII中关于大小写字母间隔为32的思考

    一直没有搞清楚为什么在ASCII中要把大小写字母的间隔设置为32,今天才发现这样设置的精妙之处:方便了程序对大小写字母进行转换.请看: ================= 十进制        32 ...

  2. 201521123036 《Java程序设计》第11周学习总结

    本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 书面作业 本次PTA作业题集多线程 互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) 1.1 除了使用 ...

  3. 多线程面试题系列(2): CreateThread与_beginthreadex本质区别

    本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...

  4. 下载安装ActiveMQ(消息队列)

    安装步骤: 第一步.安装jdk环境,因为ActiveMQ是使用java语言编写. 第二步.将下载好的activemq压缩包上传至Linux系统,进行解压. 第三步.进入解压后的bin/目录,进行启动a ...

  5. SpringMVC第四篇【参数绑定详讲、默认支持参数类型、自定义参数绑定、RequestParam注解】

    参数绑定 我们在Controller使用方法参数接收值,就是把web端的值给接收到Controller中处理,这个过程就叫做参数绑定- 默认支持的参数类型 从上面的用法我们可以发现,我们可以使用req ...

  6. DOM【介绍、HTML中的DOM、XML中的DOM】

    什么是DOM? DOM(Document Object Model)文档对象模型,是语言和平台的中立接口. 允许程序和脚本动态地访问和更新文档的内容. 为什么要使用DOM? Dom技术使得用户页面可以 ...

  7. [01] File类

    1.IO概念 File类是java.io包中一个很重要的类,这里的io,就是指 Input/Output,所以在看File类之前,先提一下所谓的IO概念. I/O(Input/Output),即输入/ ...

  8. 自定义BaseServlet利用反射

    比较完美一点的BaseServlet package com.yangwei.mvc.servlet; import java.io.IOException; import java.lang.ref ...

  9. 《Java I/O 从0到1》 - 第Ⅱ滴血 “流”

    前言 <Java I/O 从0到1>系列上一章节,介绍了File 类,这一章节介绍的是IO的核心 输入输出.I/O类库常使用流这个抽象概念.代表任何有能力产出数据的数据源对象或者是有能力接 ...

  10. Linux入门之常用命令(11)复制cp及scp

    [scp] ================== scp 命令 ================== scp 可以在 2个 linux 主机间复制文件: 命令基本格式:        scp [可选参 ...