题目链接

基本思路:最理想的方法是预处理处所有胡牌的状态的哈希值,然后对于每组输入,枚举每种新加入的牌,然后用哈希检验是否满足胡牌的条件。然而不幸的是,由于胡牌的状态数过多(4个眼+一对将),预处理的复杂度太高($O(34^5)$),因此需要想办法优化一下。

我们可以预处理出所有“加上一对将之后可以胡牌”的状态,这样预处理的复杂度就成了$O(34^4)$,在可接受的范围内了。在检验的时候,只需要枚举去掉哪一对将,就可以$O(1)$检验是否能胡牌了(有种中途相遇的感觉),另外两种特殊情况单独判断即可。

玄学优化方法:

1.在dfs和枚举检验的时候动态维护哈希值,而不是每次重复计算,这样可以节省很大一部分计算哈希值的时间。

2.dfs的时候,每一层的初始下标都不小于上一层,这样可以避免很多重复状态。

3.用哈希表代替set,可以大幅缩短存取哈希值的时间。

4.预处理处所有不少于2张的牌,这样就不用每次枚举的时候都从头开始找了。

综上,总复杂度约为$O(34^4+20000*34*7)$,应该接近极限了吧。

其实这道题也没这么复杂,直接枚举将暴力吃碰就行了,但为了锻炼自己搜索的玄学优化能力还是选择了扬长避短o( ̄▽ ̄)d

 #include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int N=+,inf=0x3f3f3f3f,M=;
const char* s="mspc";
int id[],a[][N];
ll p[][N],pm[],h;
struct D {int x,y;};
vector<D> vec,vv;
struct Hashset {
static const int N=4e5,M=1e6+;
int hd[M],nxt[N],tot;
ll p[N];
void clear() {memset(hd,-,sizeof hd),tot=;}
void insert(ll x) {
int u=x%M;
for(int i=hd[u]; ~i; i=nxt[i])if(p[i]==x)return;
p[tot]=x,nxt[tot]=hd[u],hd[u]=tot++;
}
int count(ll x) {
int u=x%M;
for(int i=hd[u]; ~i; i=nxt[i])if(p[i]==x)return ;
return ;
}
} st;
void dfs(int dep,int x,int y) {
if(st.count(h))return;
if(dep==) {st.insert(h); return;}
for(int i=x; i<=; ++i)
for(int j=(i==x?y:); j<=(i==?:); ++j) {
if(a[i][j]<=) {
a[i][j]+=,h+=*p[i][j];
dfs(dep+,i,j);
a[i][j]-=,h-=*p[i][j];
}
if(j<=&&i!=&&a[i][j]<=&&a[i][j+]<=&&a[i][j+]<=) {
a[i][j]++,a[i][j+]++,a[i][j+]++,h+=p[i][j]+p[i][j+]+p[i][j+];
dfs(dep+,i,j);
a[i][j]--,a[i][j+]--,a[i][j+]--,h-=p[i][j]+p[i][j+]+p[i][j+];
}
}
}
bool Chii() {
for(int i=; i<=; ++i)
for(int j=; j<=(i==?:); ++j)if(a[i][j]&&a[i][j]!=)return ;
return ;
}
bool Kokushi() {
for(int i=; i<=; ++i)
for(int j=; j<=(i==?:); ++j) {
if((i!=&&(j==||j==))||(i==)) {if(!a[i][j])return ;}
else if(a[i][j])return ;
}
return ;
}
bool Hu() {
for(D t:vv)if(st.count(h-*p[t.x][t.y]))return ;
return ;
}
bool ok() {return Chii()||Kokushi()||Hu();}
int main() {
st.clear();
id['m']=,id['s']=,id['p']=,id['c']=;
pm[]=;
for(int i=; i<; ++i)pm[i]=pm[i-]*M;
for(int i=,k=; i<=; ++i)
for(int j=; j<=(i==?:); ++j,--k)p[i][j]=pm[k];
dfs(,,);
int T;
for(scanf("%d",&T); T--;) {
memset(a,,sizeof a),h=;
for(int i=; i<; ++i) {
int x;
char ch;
scanf("%d%c",&x,&ch);
a[id[ch]][x]++,h+=p[id[ch]][x];
}
vec.clear(),vv.clear();
for(int i=; i<=; ++i)
for(int j=; j<=(i==?:); ++j)if(a[i][j]>=)vv.push_back({i,j});
for(int x=; x<=; ++x)
for(int y=; y<=(x==?:); ++y)if(a[x][y]<=) {
a[x][y]++,h+=p[x][y];
if(a[x][y]==)vv.push_back({x,y});
if(ok())vec.push_back({x,y});
if(a[x][y]==)vv.pop_back();
a[x][y]--,h-=p[x][y];
}
if(vec.size()) {
printf("%d",vec.size());
for(D t:vec)printf(" %d%c",t.y,s[t.x]);
puts("");
} else puts("Nooten");
}
return ;
}

还有一种极限优化的方法,因为不同花色的牌是可以独立考虑的,因此单独判断出每种花色的牌是否合法(全为3或者111),如果能胡的话,则必然有三种花色合法,一种花色不合法,其中不合法的一组必然有一对将,枚举这对将,然后判断剩下的牌是否合法即可。

 #include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int N=+,inf=0x3f3f3f3f,M=;
const char* s="mspc";
int id[],a[][N],c[N];
ll pm[],h[],hh;
struct D {int x,y;};
vector<D> vec;
struct Hashset {
static const int N=4e5,M=1e6+;
int hd[M],nxt[N],tot;
ll p[N];
void clear() {memset(hd,-,sizeof hd),tot=;}
void insert(ll x) {
int u=x%M;
for(int i=hd[u]; ~i; i=nxt[i])if(p[i]==x)return;
p[tot]=x,nxt[tot]=hd[u],hd[u]=tot++;
}
int count(ll x) {
int u=x%M;
for(int i=hd[u]; ~i; i=nxt[i])if(p[i]==x)return ;
return ;
}
} st1,st2;
void dfs1(int dep,int u) {
st1.insert(hh);
if(dep==)return;
for(int i=u; i<=; ++i) {
if(c[i]<=) {
c[i]+=,hh+=*pm[i];
dfs1(dep+,i);
c[i]-=,hh-=*pm[i];
}
if(c[i]<=&&c[i+]<=&&c[i+]<=) {
c[i]++,c[i+]++,c[i+]++,hh+=pm[i]+pm[i+]+pm[i+];
dfs1(dep+,i);
c[i]--,c[i+]--,c[i+]--,hh-=pm[i]+pm[i+]+pm[i+];
}
}
}
void dfs2(int dep,int u) {
st2.insert(hh);
if(dep==)return;
for(int i=u; i<=; ++i)if(c[i]<=) {
c[i]+=,hh+=*pm[i];
dfs2(dep+,i);
c[i]-=,hh-=*pm[i];
}
}
bool Chii() {
for(int i=; i<=; ++i)
for(int j=; j<=(i==?:); ++j)if(a[i][j]&&a[i][j]!=)return ;
return ;
}
bool Kokushi() {
for(int i=; i<=; ++i)
for(int j=; j<=(i==?:); ++j) {
if((i!=&&(j==||j==))||(i==)) {if(!a[i][j])return ;}
else if(a[i][j])return ;
}
return ;
}
bool Hu() {
int x=-;
for(int i=; i<; ++i)if(!st1.count(h[i])) {
if(~x)return ;
x=i;
}
if(!st2.count(h[])) {
if(~x)return ;
x=;
}
if(x!=) {for(int i=; i<=; ++i)if(a[x][i]>=&&st1.count(h[x]-*pm[i]))return ;}
else {for(int i=; i<=; ++i)if(a[x][i]>=&&st2.count(h[]-*pm[i]))return ;}
return ;
}
bool ok() {return Chii()||Kokushi()||Hu();}
int main() {
st1.clear(),st2.clear();
pm[]=;
for(int i=; i<; ++i)pm[i]=pm[i-]*M;
id['m']=,id['s']=,id['p']=,id['c']=;
dfs1(,),dfs2(,);
int T;
for(scanf("%d",&T); T--;) {
memset(a,,sizeof a);
memset(h,,sizeof h);
for(int i=; i<; ++i) {
int x;
char ch;
scanf("%d%c",&x,&ch);
a[id[ch]][x]++,h[id[ch]]+=pm[x];
}
vec.clear();
for(int x=; x<=; ++x)
for(int y=; y<=(x==?:); ++y)if(a[x][y]<=) {
a[x][y]++,h[x]+=pm[y];
if(ok())vec.push_back({x,y});
a[x][y]--,h[x]-=pm[y];
}
if(vec.size()) {
printf("%d",vec.size());
for(D t:vec)printf(" %d%c",t.y,s[t.x]);
puts("");
} else puts("Nooten");
}
return ;
}

HDU - 4431 Mahjong (模拟+搜索+哈希+中途相遇)的更多相关文章

  1. HDU 4431 Mahjong 模拟

    http://acm.hdu.edu.cn/showproblem.php?pid=4431 不能说是水题了,具体实现还是很恶心的...几乎优化到哭但是DFS(还加了几个剪枝)还是不行...搜索一直T ...

  2. HDU 4431 Mahjong(模拟题)

    题目链接 写了俩小时+把....有一种情况写的时候漏了...代码还算清晰把,想了很久才开写的. #include <cstdio> #include <cstring> #in ...

  3. HDU 4431 Mahjong(枚举+模拟)(2012 Asia Tianjin Regional Contest)

    Problem Description Japanese Mahjong is a four-player game. The game needs four people to sit around ...

  4. HDU 4431 Mahjong (DFS,暴力枚举,剪枝)

    题意:给定 13 张麻将牌,问你是不是“听”牌,如果是输出“听”哪张. 析:这个题,很明显的暴力,就是在原来的基础上再放上一张牌,看看是不是能胡,想法很简单,也比较好实现,结果就是TLE,一直TLE, ...

  5. HDU - 5936: Difference(暴力:中途相遇法)

    Little Ruins is playing a number game, first he chooses two positive integers yy and KK and calculat ...

  6. HDU 5936 Difference 【中途相遇法】(2016年中国大学生程序设计竞赛(杭州))

    Difference Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total ...

  7. 【LOJ6254】最优卡组 堆(模拟搜索)

    [LOJ6254]最优卡组 题面 题解:常用的用堆模拟搜索套路(当然也可以二分).先将每个卡包里的卡从大到小排序,然后将所有卡包按(最大值-次大值)从小到大排序,并提前处理掉只有一张卡的卡包. 我们将 ...

  8. 【BZOJ4524】[Cqoi2016]伪光滑数 堆(模拟搜索)

    [BZOJ4524][Cqoi2016]伪光滑数 Description 若一个大于1的整数M的质因数分解有k项,其最大的质因子为Ak,并且满足Ak^K<=N,Ak<128,我们就称整数M ...

  9. 【BZOJ4345】[POI2016]Korale 堆(模拟搜索)

    [BZOJ4345][POI2016]Korale Description 有n个带标号的珠子,第i个珠子的价值为a[i].现在你可以选择若干个珠子组成项链(也可以一个都不选),项链的价值为所有珠子的 ...

随机推荐

  1. 【HANA系列】SAP HANA SQL条件判断是NULL的写法

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA SQL条件判断 ...

  2. CTR点击率校准

    1. 概述 广告CTR预估过程中,正负样本比例差距较大,需要采样,但是采用后模型训练的结果是有偏的. 2. 校准方式 用逻辑回归作为激活函数

  3. FTL2

    ABSTACT 1.NAND flash memory  (主要缺点): (1)partial page updates (2)general-purpose cache usually does n ...

  4. Elasticsearch-更新现有文档

    ES-更新现有文档 ES的更新API允许发送文档所需要做的修改,而且API会返回一个答复,告知操作是否成功.更新流程如下 1. 检索现有的文档.为了使这步奏效,必须打开_source字段,否则ES并不 ...

  5. [Python3] 039 语法调试

    目录 语法调试 1. 调试技术 2. pdb 调试 插一个 gdb 3.Pycharm 调试 4. 单元测试 语法调试 1. 调试技术 调试流程 单元测试 → 集成测试 → 交测试部 分类: 静态调试 ...

  6. [转帖]$PWD 和 $(pwd)

    $PWD 和 $(pwd) https://blog.csdn.net/shaojwa/article/details/51894980 细节决定成败.   注意两个效果一样,但是注意大小写,PWD是 ...

  7. 在 sys.servers 中找不到服务器 '10.0.2.13'。请验证指定的服务器名称是否正确。

    工作中,因为需要,搭建同事的程序模块,附加了从同事那里拷过来的该程序使用的库.(C#.C/S..Net Framework4.0 .WCF.Win10.SQL Server 2014.VS2015) ...

  8. 最长相同01数的子串(map搞搞)--牛客第三场 -- Crazy Binary String

    题意: 如题. 或者用我的数组分治也可以,就是有点愚蠢. //#include <bits/stdc++.h> #include <map> #include <iost ...

  9. CVE-2017-17558漏洞学习

    简介 这是USB core中的一个拒绝服务漏洞.带有精心设计的描述符的恶意USB设备可以通过在配置描述符中设置过高的bNumInterfaces值来导致内核访问未分配的内存.虽然在解析期间调整了该值, ...

  10. Mysql-Sqlalchemy-增删改查分组等操作

    #!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy.ext.declarative import declarative_base ...