您的位置:首页 > Web前端 > JavaScript

[BZOJ 1028][JSOI 2007] 麻将 模拟+贪心思想

2017-10-25 20:57 295 查看
题目传送门:【BZOJ 1028】

题目大意: 这里,我们考虑一种特殊的麻将。在这种特殊的麻将里,没有字牌,花色也只有一种。但是,序数不会被限制在 1 到 9 的范围内,而是在 1 到 n 的范围内。同时,也没有每一种牌恰好四张的限制。一组和了的牌由 3m + 2 张牌组成,其中两张组成对子,其余 3m 张组成三张一组的 m 组牌,每组须为顺子(连续的三张牌)或刻子(相同的三张牌)。现给出一组 3m + 1 张的牌,要求判断该组牌是否为听牌(即还差一张牌就可以和牌)。如果是的话,按顺序从小到大输出所有可能的等待牌,否则输出 NO。(9 ≤ n ≤ 400,4 ≤ m ≤ 1000 )

题目分析:

不明白为什么省选题还只有这个难度……

由题,这道题数据规模仅为 n ≤ 400,因此 O(n3) 的暴力算法也可以轻松跑过。所以我们直接上模拟即可。

大体思路:我们要遵循“模拟每次要从最简单的地方上手”的原则,于是我们在按顺序枚举 1-n 之内的数是否为听牌序号时,先依次判断 1-n 内的对子;如果此时有任意一种牌的数量 ≥3, 我们就先把多余的牌当作刻子打出,最后再考虑顺子的情况,如果有负数出现说明不合法。

注意每次要考虑 n+1,n+2 是否为负数;在开始新的枚举时,一定要把原来的记录给抹掉。

下面附上代码:

[cpp] view plain copy print?#include<cstdio>
#include<cstring>
const int MX=405;

int n,m,totm,q[MX],tl=0;
int cnt[MX],tmp[MX];

bool check(int x){
for (int i=1;i<=n;i++){ //枚举最后剩下的对子
bool ok=true;
memcpy(tmp,cnt,sizeof(int)*(n+5));
tmp[x]++;
if (tmp[i]>=2){
tmp[i]-=2;
for (int j=1;j<=n+2;j++){ //检验当前牌的状态
if (tmp[j]<0){
ok=false;
break;
}
if (tmp[j]%=3){ //优先打出刻子
tmp[j+1]-=tmp[j],tmp[j+2]-=tmp[j];
tmp[j]=0;
}
}
if (ok)
return true;
}
}
return false;
}

int main(){
int a;
scanf(”%d%d”,&n,&m);
for (int i=1;i<=3*m+1;i++){
scanf(”%d”,&a);
cnt[a]++;
}
for (int i=1;i<=n;i++){ //判断听牌为 i 时是否有解
if (check(i))
q[++tl]=i;
}
if (tl==0) printf(“NO”);
else {
for (int i=1;i<tl;i++) printf(“%d ”,q[i]);
printf(”%d”,q[tl]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: