Luogu 2668 NOIP 2015 斗地主(搜索,动态规划)
Luogu 2668 NOIP 2015 斗地主(搜索,动态规划)
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=2668
Source
搜索,动态规划
解决思路
题目中给出的出牌规则比较复杂,我们简单地分一下类
第一类:牌的数字与出牌方式没有关系:单张,对子,三张,双王,三带一,三带二,四带二,炸弹
第二类:单顺子,双顺子,三顺子
我们发现,如果只按照第一类的方式打出,当每一种牌(有一张的,有两张的,有三张的,有四张的)的数量一定时,最少的出牌步数是一定的。所以我们考虑把这个先预处理出来,然后搜索顺子的情况。
设\(F[i][j][k][l]\)表示单张\(i\)张,对子\(j\)对,三张的\(k\)组,炸弹\(l\)组时的最少步数。我们有下面的转移方程:(注意,都要在有意义的情况下转移,比如说\(i,j,k,l\)不能出现负值)
考虑单张
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][j-1][k][l]+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-1][j][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][j][k][l-1]+1);//打出四张牌
F[i][j][k][l]=min(F[i][j][k][l],F[i-2][j][k][l-1]+1);//打出四带两张单牌
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-2][k][l-1]+1);//打出四带两对牌
然后我们搜索顺子的情况。可以作为顺子的是3~A,所以为了方便,我们在输入的时候把1变成14。注意,顺子可以不打完,双顺子可以拆成单顺子,三顺子可以拆成单顺子或双顺子。最后统计最优解。
另外需要注意的是,两张王既可以看作两张单牌,也可以看作一对子。
本题加强版:http://www.cnblogs.com/SYCstudio/p/7628971.html
代码
#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);//计算当前如果只按照第一类方式打出牌的最少步数,x1~x4分别是1张~4张牌的数量
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)//将'A'单独拿出来,变成14,方便处理顺子
Card[14]++;
else
Card[a]++;
}
dfs(0);
printf("%d\n",Ans);
}
return 0;
}
void init()//计算出F
{
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 (i+2*j+3*k+4*l<=n)
{
if (i!=0)//一张牌
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k][l]+1);//打出一个单牌
if (j!=0)//双牌
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k][l]+1);//打出一个对子
if (k!=0)//三张牌
{
F[i][j][k][l]=min(F[i][j][k][l],F[i][j][k-1][l]+1);//打出三张牌
if (i!=0)
F[i][j][k][l]=min(F[i][j][k][l],F[i-1][j][k-1][l]+1);//打出三带一
if (j!=0)
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-1][k-1][l]+1);//打出三带二
}
if (l!=0)//四张牌
{
F[i][j][k][l]=min(F[i][j][k][l],F[i][j][k][l-1]+1);//打出四张牌
if (i>=2)
F[i][j][k][l]=min(F[i][j][k][l],F[i-2][j][k][l-1]+1);//打出四带两张单牌
if (j>=2)
F[i][j][k][l]=min(F[i][j][k][l],F[i][j-2][k][l-1]+1);//打出四带两对牌
}
//printf("(%d,%d,%d,%d) %d\n",i,j,k,l,F[i][j][k][l]);
}
}
return;
}
void dfs(int step)//搜索顺子,step表示当前打出多少个顺子
{
if (step>Ans)//最优性剪枝
return;
int Cnt[maxN];
memset(Cnt,0,sizeof(Cnt));//记录有1张~4张的牌分别有多少种
for (int i=2;i<=14;i++)//注意这里要统计2
Cnt[Card[i]]++;
Ans=min(Ans,step+now_step(Cnt[1],Cnt[2],Cnt[3],Cnt[4]));//计算当前剩余的都按照第一类方式打出的最少步数
for (int k=1;k<=3;k++)//枚举顺子or双顺子or三顺子
{
for (int i=3;i<=14;i++)//注意这里从3开始
{
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[x1][x2][x3][x4];//没有王
if (Card[0]==1)
return F[x1+1][x2][x3][x4];//只有一个王
if (Card[0]==2)
return min(F[x1+2][x2][x3][x4],F[x1][x2][x3][x4]+1);//将两张王看作两张单牌or一对牌
}
Luogu 2668 NOIP 2015 斗地主(搜索,动态规划)的更多相关文章
- 洛谷 P2668 & P2540 [ noip 2015 ] 斗地主 —— 搜索+贪心
题目:https://www.luogu.org/problemnew/show/P2668 https://www.luogu.org/problemnew/show/P2540 首先,如果没有 ...
- Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)
Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分) Description L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之 ...
- 基础算法(搜索):NOIP 2015 斗地主
Description 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗地主中,牌的大小关系根据牌的数码表示如下:3& ...
- [NOIp 2015]斗地主
Description 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗地主中,牌的大小关系根据牌的数码表示如下:3& ...
- [BZOJ 4325][NOIP 2015] 斗地主
一道防AK好题 4325: NOIP2015 斗地主 Time Limit: 30 Sec Memory Limit: 1024 MBSubmit: 820 Solved: 560[Submit] ...
- [NOIP 2015] 斗地主 landlord
想起几个月之前的 noip2015-只会瞎搞-这道题骗了 30 分.T T 题目 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的 A 到 K 加上大小王的共 54 张牌 ...
- noip 2015 斗地主 大爆搜!!!
反正肯定是大模拟 但是每一个可以出的牌都搜一定不是最优的 考虑最特殊的出牌方案:顺子(单,对,三) 每一种方案再加上暴力贪心打出剩下的牌的步数 #include<cstdio> #incl ...
- Luogu 1979 NOIP 2013 华容道(搜索,最短路径)
Luogu 1979 NOIP 2013 华容道(搜索,最短路径) Description 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面 ...
- NOIP 2015
Prob.1 2015 神奇的幻方 模拟就好了.(这不是noip2017的初赛题么.)代码: #include<cstdio> #include<cstring> #inclu ...
随机推荐
- SqlServer 案例:已有汽车每日行驶里程数据,计算其每日增量
需求说明 某公司某项业务,需要获得用户每日行车里程数.已知能获得该车每日提交的总里程数,如何通过 T-SQL 来获得其每日增量里程? 解决方案 首选需要对数据进行编号,利用开窗函数 OVER() 实现 ...
- HTTP Error 500.22 - Internal Server Error 错误解决方案
1. 首先进入IIS ,配置IIS 应用程序池的.Net Framework版本 2. 点击左侧应用程序池,再单机右侧设置,选择版本 3. 设置为经典模式 如若遇到以下错误: 解决方案:删除confi ...
- mysql操作命令梳理(1)-索引
1.创建索引索引的创建可以在CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER TABLE来给表增加索引.以下命令语句分别展示了如何创建主键索引(PRIMARY KE ...
- Centos7.2下OpenVPN 环境完整部署记录
关于OpenVPN的有关介绍及为何使用OpenVPN在此就不做赘述了,下面直接记录Centos7.2系统下部署OpenVPN环境的操作过程: 1) 先将本机的yum换成阿里云的yum源 [root@t ...
- zabbix问题记录
zabbix部署好,在使用一段时间后,出现了不少报错,在此简单做一记录.1)Zabbix监控界面报错Lack of free swap space on Zabbix server”解决公司线上部署的 ...
- 牛客多校第三场-A-PACM Team-多维背包的01变种
题目我就不贴了...说不定被查到要GG... 题意就是我们需要在P,A,C,M四个属性的限制下,找到符合条件的最优解... 这样我们就需要按照0/1背包的思路,建立一个五维度数组dp[i][j][k] ...
- js 时间戳转换为日期格式
//将1525854409000类型的时间转换成“yyyy-MM-dd”或“yyyy-MM-dd hh:mm:ss” //info.birthday是后台获取到的Date类型的出生日期数据, / ...
- 个人作业 Last
对M1/M2阶段的总结 M1阶段的总结反思见我以前的博客,我以前曾经写过.现附上链接.http://www.cnblogs.com/jirufeng/p/4990245.html M2阶段主要是对我们 ...
- Linux内核分析— —操作系统是如何工作的(20135213林涵锦)
mykernel实验指导(操作系统是如何工作的) 实验要求 运行并分析一个精简的操作系统内核,理解操作系统是如何工作的 使用实验楼的虚拟机打开shell cd LinuxKernel/linux-3. ...
- Linux内核读书笔记第三周 调试
内核调试的难点在于它不能像用户态程序调试那样打断点,随时暂停查看各个变量的状态. 也不能像用户态程序那样崩溃后迅速的重启,恢复初始状态. 用户态程序和内核交互,用户态程序的各种状态,错误等可以由内核来 ...