Luogu 2540 斗地主增强版(搜索,动态规划)

Description

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

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

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

具体规则如下:

Input

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

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

Output

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

Sample Input

1 8

7 4

8 4

9 1

10 4

11 1

5 1

1 4

1 1

Sample Output

3

Http

Luogu:https://www.luogu.org/problem/show?pid=2540

Source

搜索,动态规划

解决思路

这是这一题的增强版。如果将原题的程序直接交到这里,只能获得76分。

为什么呢?因为我们没有考虑把三张的或四张的拆开来计算,比如下面这个例子

1 1 1 1 2 2 2 2

如果按照原来看作两个炸弹的观点,需要两步打出。但如果将其中一个炸弹拆成两对,将这8张牌看作4带2对的话,可以一步打出。

所以在上一题的基础上,我们在预处理的时候要加上几个条件。

但因为若按照原来按照\(F[i][j][k][l]\)表示单张\(i\)张,对子\(j\)组,三张的\(k\)组,四张的\(l\)组,并按照原来的循环顺序操作的话,会出现计算\(F[i][j][k][l]\)的时候需要\(F[i+1][……]\)的情况,所以考虑到4张可以拆成3张、2张、1张,3张可以拆成2张、1张,所以我们把\(i,j,k,l\)调换顺序,设\(F[i][j][k][l]\)表示炸弹\(l\)组,三张的\(k\)组,对子\(j\)组,单张\(i\)组,然后转移

一张
F[i][j][k][l]=min(F[i][j][k][l],F[i][j][k][l-1]+1);//打出一个单牌
两张
F[i][j][k][l]=min(F[i][j][k][l],F[i][j][k-1][l]+1);//打出一个对子
三张
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k][l]+1);//打出三张牌
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k][l-1]+1);//打出三带一
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k-1][l]+1);//打出三带二
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k+1][l+1]);//新增:三个拆成一对和一个
四张
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k][l]+1);//打出四张牌
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k][l-2]+1);//打出四带两张单牌
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k-2][l]+1);//打出四带两对牌
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j+1][k][l+1]);//新增:四个拆成三个和一个
F[i][j][k][l]=min(F[i][j][k][l],F[i-2][j][k][l]+1);//新增:两个四拆成一个四带两对
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k-1][l]+1);//新增:四带一对

其他的地方与原题代码一致,请看这里

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std; const int maxN=25;
const int Shunzi[4]={0,5,3,2};//i顺子至少需要多少张牌
const int inf=2147483647; int n;
int Card[maxN];
int F[maxN][maxN][maxN][maxN];
int Ans; void init();
void dfs(int step);
int now_step(int x1,int x2,int x3,int x4); int main()
{
int T;
scanf("%d%d",&T,&n);
init();
while (T--)
{
memset(Card,0,sizeof(Card));
Ans=n;
for (int i=1;i<=n;i++)//输入
{
int a,b;
scanf("%d%d",&a,&b);
if (a==0)//0是王
Card[0]++;
else
if (a==1)
Card[14]++;
else
Card[a]++;
}
dfs(0);
printf("%d\n",Ans);
}
return 0;
} void init()
{
F[0][0][0][0]=0;
for (int i=0;i<=n;i++)
for (int j=0;j<=n;j++)
for (int k=0;k<=n;k++)
for (int l=0;l<=n;l++)
{
F[i][j][k][l]=i+j+k+l;
if (4*i+3*j+2*k+l<=n)
{
if (l!=0)//一张牌
F[i][j][k][l]=min(F[i][j][k][l],F[i][j][k][l-1]+1);//打出一个单牌
if (k!=0)//双牌
F[i][j][k][l]=min(F[i][j][k][l],F[i][j][k-1][l]+1);//打出一个对子
if (j!=0)//三张牌
{
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k][l]+1);//打出三张牌
if (l!=0)
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k][l-1]+1);//打出三带一
if (k!=0)
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k-1][l]+1);//打出三带二
if (j>=1)
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k+1][l+1]);//三个拆成一对和一个
}
if (i!=0)//四张牌
{
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k][l]+1);//打出四张牌
if (l>=2)
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k][l-2]+1);//打出四带两张单牌
if (k>=2)
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k-2][l]+1);//打出四带两对牌
if (i>=1)
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j+1][k][l+1]);//四个拆成三个和一个
if (i>=2)
F[i][j][k][l]=min(F[i][j][k][l],F[i-2][j][k][l]+1);//两个四拆成一个四带两对
if (k>=1)
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k-1][l]+1);//四带一对
}
//printf("(%d,%d,%d,%d) %d\n",i,j,k,l,F[i][j][k][l]);
}
}
return;
} void dfs(int step)
{
if (step>Ans)
return;
int Cnt[maxN];
memset(Cnt,0,sizeof(Cnt));
for (int i=2;i<=14;i++)//统计1张的、2张的、3张的和4张的分别有几组
Cnt[Card[i]]++;
Ans=min(Ans,step+now_step(Cnt[1],Cnt[2],Cnt[3],Cnt[4]));//统计当前剩下的都按照非顺子的方式打出去
//cout<<Ans<<endl;
for (int k=1;k<=3;k++)//枚举当前选择k顺子
{
for (int i=3;i<=14;i++)//枚举起始位置
{
int pos;
for (pos=i;(pos<=14)&&(Card[pos]>=k);pos++)//枚举顺子结束位置
{
Card[pos]=Card[pos]-k;
if (pos-i+1>=Shunzi[k])
dfs(step+1);
}
for (pos=pos-1;pos>=i;pos--)//将牌还原
Card[pos]=Card[pos]+k;
}
}
return;
} int now_step(int x1,int x2,int x3,int x4)
{
if (Card[0]==0)
return F[x4][x3][x2][x1];//没有王
if (Card[0]==1)
return F[x4][x3][x2][x1+1];//只有一个王
if (Card[0]==2)
return min(F[x4][x3][x2][x1+2],F[x4][x3][x2][x1]+1);//将两张王看作两张单牌or一对牌
}

Luogu 2540 斗地主增强版(搜索,动态规划)的更多相关文章

  1. [Luogu2540][NOIP2016]斗地主增强版(搜索+DP)

    增强版就是原版中两鬼不算对子的版本. 先爆搜出完所有对子,剩下的牌DP处理. 考虑每个数码的拆牌情况,最多可能被拆成5种情况:1+1+1+1,1+1+2,1+3,2+2,4.故DP状态数最多为5^13 ...

  2. P2540 斗地主增强版

    P2540斗地主增强版 参考大佬题解 思路:顺子暴力搜,剩下的牌我不会贪心所以用记忆化搜索(或者dp): 注意:双王不能当对,二不算顺子 代码 #include <cstdio> #inc ...

  3. 洛谷 题解 P2540 【斗地主增强版】

    [分析] 暴力搜顺子,贪心出散牌 为什么顺子要暴力? 玩过斗地主的都知道,并不是出越长的顺子越好,如果你有一组手牌,3,4,5,6,7,6,7,8,9,10,你一下把最长的出了去,你会单两张牌,不如出 ...

  4. Luogu2540 斗地主增强版(搜索+动态规划)

    单纯的暴搜似乎还是很好写的,然而过不了.出完顺子之后答案是可以dp出来的,于是大力搜然后大力dp就好了. dp时强行讨论完了几乎所有拆牌情况,理性愉悦一发. #include<iostream& ...

  5. luogu2540 斗地主增强版

    题目大意 给你一副手牌,没有飞机带翅膀,按斗地主的规则,求将所有牌打出的最少次数. 题解 先不考虑顺子 我们已经知道花色对牌没有影响,那么如果不考虑顺子,每个牌具体是什么数字我们也用不着知道,我们关心 ...

  6. 【NOIP2015】斗地主 D1 T3 及 增强版 (送命题)

    恶心送命模拟题 暴搜顺子,DP预处理剩下的. 由于官方数据太水,很多情况没有讨论的都能过普通版本,想要测试自己代码正确性的同学们可以交交这道题,有很多dalao给出了hack数据 : Luogu P2 ...

  7. Luogu P1276 校门外的树(增强版)

    Luogu P1276 校门外的树(增强版) 本来看着是道普及-,就不打算写博客了,结果因为出了3次错,调试了15min就还是决定写一下-- 本题坑点: 1.每个位置有三种情况:空穴,树苗,树(而不只 ...

  8. 《zw版·delphi与halcon系列原创教程》zw版_THOperatorSetX控件函数列表 v11中文增强版

    <zw版·delphi与halcon系列原创教程>zw版_THOperatorSetX控件函数列表v11中文增强版 Halcon虽然庞大,光HALCONXLib_TLB.pas文件,源码就 ...

  9. 微慕WordPress小程序增强版

    2017年1月9日,张小龙在2017微信公开课Pro上发布的微信小程序正式上线.在过去的2年多的时间里,微信小程序领头,各大互联网平台也不甘落后,陆续推出自己的小程序.2018年7月4日,百度智能小程 ...

随机推荐

  1. Visual Studio2017 数据库架构比较

    一.前言 开发的时候在测试服务器上和线网服务器上面都有我们的数据库,当我们在线网上面修改或者新增一些字段后,线网的数据库也需要更新,这个时候根据表的修改记录,然后在线网上面一个一个增加修改很浪费效率而 ...

  2. Visual studio2015 编译时提示“GenerateResource”任务意外失败。

    今天弄了一个winfrom程序,狗血,一直报错,在另一台电脑上就不报错. 错误如下图 其实这样也能运行,但就是代码改之后,没有办法调试.搜了很久,发现了一种解决办法,完美解决. 最终成功了.

  3. apacheTomcat

    Window+R ------>cmd || Window PowerShell apacheTomcat\bin> ./startup.sh

  4. BugPhobia开发篇章:Beta阶段第IX次Scrum Meeting

    0x01 :Scrum Meeting基本摘要 Beta阶段第九次Scrum Meeting 敏捷开发起始时间 2015/12/25 00:00 A.M. 敏捷开发终止时间 2015/12/28 23 ...

  5. Scrum Meeting NO.8

    Scrum Meeting No.8 1.会议内容 2.任务清单 徐越 序号 近期的任务 进行中 已完成 1 代码重构:前端通讯模块改为HttpClient+Json √ 2 添加对cookies的支 ...

  6. 第六次Scrum meeting

    第六次Scrum  meeting 任务及完成度: 成员 12.21 12.22 陈谋 任务1040:完成stackoverflow的数据处理后的json处理(98%) 任务1114-1:完成对网页数 ...

  7. sring引入mybatis

    1.首先框架结构是这样的(jar包还是要导的) 2.web.xml和springMVC-servlet.xml未作任何新的配置,这里简单贴一下代码: <?xml version="1. ...

  8. BUAA软工个人作业Week3-案例分析

    一. 调研评测 评测项目:为了联系移动和PC版,我同时下载了必应词典的Android版本和UWP版本,选择UWP的原因是想看看微软推广的UWP在微软自己的应用上的效果.当然主要是对安卓的测评(UWP用 ...

  9. mac安装sublime text 3,含注册码

    软件下载地址: https://www.sublimetext.com/3 注册码如下: —– BEGIN LICENSE —– TwitterInc 200 User License EA7E-89 ...

  10. 文本文件合并(C++实现)

    直接附上代码吧 #include<iostream> #include<fstream> #include<cstdlib> using namespace std ...