题目传送门:【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 是否为负数;在开始新的枚举时,一定要把原来的记录给抹掉。
下面附上代码:
- #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;
- }