【分析】

暴力搜顺子,贪心出散牌

为什么顺子要暴力?

玩过斗地主的都知道,并不是出越长的顺子越好,如果你有一组手牌,3,4,5,6,7,6,7,8,9,10,你一下把最长的出了去,你会单两张牌,不如出两个顺子,所以顺子要暴力。

贪心打散牌

这是核心所在,也是不超时的原因。

可以先统计一下不同牌个数的组数,然后再出牌,

那如何打出最优解?

首先一定要先出四带二,再出三带一,这是很容易想到的,因为四带可以带走两张。

这样写正的行了吗?

当然不行,原题的随机数额可以过,增强版的必须考虑拆牌,而且还要考虑王炸的特殊情况,王算单牌,但可以当对出,明确这一点。

拆牌

什么时候该拆牌呢,对牌除了四带的情况不能拆,因为拆了可能多打一手牌。

仔细想想,三张和炸在单牌和对牌很多的时候是不能拆的,拆了就多大。

当单牌和对牌数的和小于三张和炸的和,这就可以拆了

因为这时不拆的话没得带,只能单出,如果把三张拆成一单和一对,让其余的三张和炸带走,就会少一步。

四张的同理。

具体贪心过程(按优先级排序)

  • 把一个炸拆成3张和单牌,再出一组四带二单和三带一

  • 把一组三张拆成一对和一单,再出一组四带二单和三带二

  • 三四张的比单牌和对牌多,拆着打

  • 还多继续拆

  • 四带两单

  • 四带两对

  • 对看成两单再四带

  • 三张的太多了拆三张

  • 把一组三张拆成单和对,再出三带一和三带二

  • 三带一

  • 三带二

还剩三张和炸,组合出

  • 把两个三张拆成两个对和两个单,再出四带两对和四带两单

  • 把一个炸拆成一对和两单,再出三带二和四带两单

  • 把一个炸拆成两对,再出两组三带一对

  • 同上,把一组三张拆成单和对,再出三带一和三带二

  • 把一个炸拆成两对,再出一组四带两对

  • 有双王一块出

  • 出单牌

【代码】

#include<bits/stdc++.h>
using namespace std;
int T,n;
int pai[15];
int ans=0x3f3f3f3f;
inline int read()
{
int tot=0,f=1;char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
tot=tot*10+c-'0';
c=getchar();
}
return tot*f;
}
inline bool check()
{
for(int i=1;i<=14;i++)
if(pai[i])return false;
return true;
}
inline int sp()
{
bool flag=(pai[14]==2?1:0);
int c[5],tot=0;
memset(c,0,sizeof(c));
c[1]+=pai[14];
for(int i=1;i<=13;i++)c[pai[i]]++;
/*for(int i=1;i<=4;i++)
cout<<c[i]<<" ";
cout<<endl;*/
while(c[3]==0&&c[1]==1&&c[2]==1&&c[4]>1)tot+=2,c[4]-=2,c[1]--,c[2]--;
while(c[2]==0&&c[1]==1&&c[4]==1&&c[3]>1)tot+=2,c[3]-=2,c[1]--,c[4]--;
if(c[3]+c[4]>c[1]+c[2])while(c[4]>0&&c[2]>0&&c[3]>0)tot++,c[2]--,c[3]--,c[1]++,c[4]--;
if(c[3]+c[4]>c[1]+c[2])while(c[4]>0&&c[1]>0&&c[3]>0)tot++,c[1]--,c[3]--,c[2]++,c[4]--;
while(c[4]>0&&c[1]>1)tot++,c[4]--,c[1]-=2;
while(c[4]>0&&c[2]>1)tot++,c[4]--,c[2]-=2;
while(c[4]>0&&c[2]>0)tot++,c[4]--,c[2]--;
if(c[3]%3==0&&c[1]+c[2]<=1)while(c[3]>2)tot+=2,c[3]-=3;
while(c[3]>0&&c[2]>0)tot++,c[3]--,c[2]--;
while(c[3]>0&&c[1]>0)tot++,c[3]--,c[1]--;
while(c[4]>1&&c[3]>1)tot+=2,c[3]-=2,c[4]-=2;
while(c[4]>1&&c[3]>0)tot+=2,c[3]--,c[4]-=2;
while(c[3]>1&&c[4]>0)tot+=2,c[4]--,c[3]-=2;
while(c[4]>1)tot++,c[4]-=2;
while(c[3]>2)tot+=2,c[3]-=3;
/*for(int i=1;i<=4;i++)
cout<<c[i]<<" ";
cout<<endl;*/
if(c[1]>=2&&flag)return tot+c[2]+c[3]+c[4]+c[1]-1;
else return tot+c[1]+c[2]+c[3]+c[4];
}
inline void dfs(int cnt)
{
if(cnt>=ans)return;
if(check())
{
ans=min(ans,cnt);
}
int temp=sp();
/*for(int i=1;i<=14;i++)
cout<<pai[i]<<" ";cout<<endl; */
ans=min(temp+cnt,ans);
for(int i=1;i<=12;i++)
{
if(pai[i]<3)continue;
for(int j=i+1;j<=12;j++)
{
if(pai[j]<3)break;
if(j-i+1>=2)
{
for(int k=i;k<=j;k++)pai[k]-=3;
dfs(cnt+1);
for(int k=i;k<=j;k++)pai[k]+=3;
}
}
}
for(int i=1;i<=12;i++)
{
if(pai[i]<2)continue;
for(int j=i+1;j<=12;j++)
{
if(pai[j]<2)break;
if(j-i+1>=3)
{
for(int k=i;k<=j;k++)pai[k]-=2;
dfs(cnt+1);
for(int k=i;k<=j;k++)pai[k]+=2;
}
}
}
for(int i=1;i<=12;i++)
{
if(!pai[i])continue;
for(int j=i+1;j<=12;j++)
{
if(!pai[j])break;
if(j-i+1>=5)
{
for(int k=i;k<=j;k++)pai[k]--;
dfs(cnt+1);
for(int k=i;k<=j;k++)pai[k]++;
}
}
}
}
int main()
{
//freopen("testdata.in","r",stdin);
T=read();n=read();
while(T--)
{
memset(pai,0,sizeof(pai));
ans=0x3f3f3f3f;
int ds,hs;
for(int i=1;i<=n;i++)
{
ds=read();hs=read();
if(ds>=3&&ds<=13)pai[ds-2]++;
else if(ds==0&&hs==1)pai[14]++;
else if(ds==0&&hs==2)pai[14]++;
else pai[ds+11]++;
}
dfs(0);
cout<<ans<<endl;
}
return 0;
}

参考博客:https://www.luogu.org/blog/user40673/solution-p2540

洛谷 题解 P2540 【斗地主增强版】的更多相关文章

  1. P2540 斗地主增强版

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

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

    Luogu 2540 斗地主增强版(搜索,动态规划) Description 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游 ...

  3. 洛谷 P2668 & P2540 [ noip 2015 ] 斗地主 —— 搜索+贪心

    题目:https://www.luogu.org/problemnew/show/P2668   https://www.luogu.org/problemnew/show/P2540 首先,如果没有 ...

  4. 洛谷 题解 UVA572 【油田 Oil Deposits】

    这是我在洛谷上的第一篇题解!!!!!!!! 这个其实很简单的 我是一只卡在了结束条件这里所以一直听取WA声一片,详细解释代码里见 #include<iostream> #include&l ...

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

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

  6. 洛谷 题解 P1600 【天天爱跑步】 (NOIP2016)

    必须得说,这是一道难题(尤其对于我这样普及组205分的蒟蒻) 提交结果(NOIP2016 天天爱跑步): OJ名 编号 题目 状态 分数 总时间 内存 代码 / 答案文件 提交者 提交时间 Libre ...

  7. 洛谷题解P4314CPU监控--线段树

    题目链接 https://www.luogu.org/problemnew/show/P4314 https://www.lydsy.com/JudgeOnline/problem.php?id=30 ...

  8. luogu2540 斗地主增强版

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

  9. 洛谷 题解 P3385 【【模板】负环】

    一.声明 在下面的描述中,未说明的情况下,\(N\) 是顶点数,\(M\)是边数. 二.判负环算法盘点 想到判负环,我们会想到很多的判负环算法.例如: 1. Bellman-Ford 判负环 这个算法 ...

随机推荐

  1. 2017.10.3 国庆清北 D3T3 解迷游戏

    题目描述 LYK进了一家古董店,它很想买其中的一幅画.但它带的钱不够买这幅画. 幸运的是,老板正在研究一个问题,他表示如果LYK能帮他解出这个问题的话,就把这幅画送给它. 老板有一个n*m的矩阵,他想 ...

  2. 【概率论】5-8:Beta分布(The Beta Distributions)

    title: [概率论]5-8:Beta分布(The Beta Distributions) categories: - Mathematic - Probability keywords: - Th ...

  3. 编译安装和二进制安装mysql

    二进制安装mysql-5.6.46 mysql二进制安装,已经编译成二进制了,只需要做一些配置即可 [root@localhost ~]$ yum install autoconf libaio -y ...

  4. 《挑战30天C++入门极限》新手入门:C/C++中数组和指针类型的关系

        新手入门:C/C++中数组和指针类型的关系 对于数组和多维数组的内容这里就不再讨论了,前面的教程有过说明,这里主要讲述的数组和指针类型的关系,通过对他们之间关系的了解可以更加深入的掌握数组和指 ...

  5. SQL 清理日志

    USE[master] GO ALTER DATABASE 要清理的数据库名称 SET RECOVERY SIMPLE WITH NO_WAIT GO ALTER DATABASE 要清理的数据库名称 ...

  6. 模板 - 数学 - 数论 - Min_25筛

    终于知道发明者的正确的名字了,是Min_25,这个筛法速度为亚线性的\(O(\frac{n^{\frac{3}{4}}}{\log x})\),用于求解具有下面性质的积性函数的前缀和: 在 \(p\) ...

  7. avalon如何用年月日的方式输出..

    在avolon里面的http://avalonjs.coding.me/filter.html  可以找到与date相关的转化,如果是要转化为年月日的形式,看下面的代码: <span style ...

  8. 常用spaceclaim脚本(三)

    拉伸曲线 ptList=List[Point]() #定义一个点的列表 ptList.Add(Point.Create(MM(11),MM(-14),MM(0))) #创建点,并放入列表当中 ptLi ...

  9. Vue 一个组件引用另一个组件

    有些时候需要这么做,比如,我想在首页加载轮播组件,但是又不想全局注册(因为不是每个页面都需要轮播功能) 方法1: <template> <div> <!-- 3.在tem ...

  10. Python脚本基础运算和算法

    原文地址:https://www.cnblogs.com/ailiailan/p/10141741.html 通过关注“常见”脚本,是对代码的一个很好的学习和总结的方式. 1.冒泡排序 lis = [ ...