模拟不要怎么搞都暴搜!
P4050 [JSOI2007] 麻将
题面:
题目描述
麻将是中国传统的娱乐工具之一。麻将牌的牌可以分为字牌(共有东、南、西、北、中、发、白七种)和序数牌(分为条子、饼子、万子三种花色,每种花色各有一到九的九种牌),每种牌各四张。
在麻将中,通常情况下一组和了的牌(即完成的牌)由十四张牌组成。十四张牌中的两张组成对子(即完全相同的两张牌),剩余的十二张组成三张一组的四组,每一组须为顺子(即同花色且序数相连的序数牌,例如条子的三、四、五)或者是刻子(即完全相同的三张牌)。一组听牌的牌是指一组十三张牌,且再加上某一张牌就可以组成和牌。那一张加上的牌可以称为等待牌。
在这里,我们考虑一种特殊的麻将。在这种特殊的麻将里,没有字牌,花色也只有一种。但是,序数不被限制在一到九的范围内,而是在1到n的范围内。同时,也没有每一种牌四张的限制。一组和了的牌由3m + 2张牌组成,其中两张组成对子,其余3m张组成三张一组的m组,每组须为顺子或刻子。现给出一组3m + 1张的牌,要求判断该组牌是否为听牌(即还差一张就可以和牌)。如果是的话,输出所有可能的等待牌。
输入格式
包含两行。第一行包含两个由空格隔开整数n, m (9<=n<=400, 4<=m<=1000)。第二行包含3m + 1个由空格隔开整数,每个数均在范围1到n之内。这些数代表要求判断听牌的牌的序数。
输出格式
输出为一行。如果该组牌为听牌,则输出所有的可能的等待牌的序数,数字之间用一个空格隔开。所有的序数必须按从小到大的顺序输出。如果该组牌不是听牌,则输出"NO"。
样例 #1
样例输入 #1
9 4 1 1 2 2 3 3 5 5 5 7 8 8 8
样例输出 #1
6 7 9
暴搜只能喜提 10 ∼ 20 10 \sim 20 10∼20 分
缺字,我们补上,判断是否合法。(摈弃一些正向思维)
我们再来看牌型的影响,对子、刻子 影响的是本身,而顺子影响的是连续的 3 3 3 个数.
由于牌中只能有一个对子,我们考虑扣掉,枚举对子,判断是否合法
当我们判断到 x x x 时,如果 x x x 可以凑成刻子,毫不犹豫凑成,因为他不影响到 x + 1 , x + 2 x + 1,x + 2 x+1,x+2 这些位置,
我们枚举顺子的起点为 i i i ,
因为 如果到 i + 1 i + 1 i+1 后, i i i 位置还没有清完,说明失败了!
所以我们会不惜一切代价将凑完刻子的剩余的 i i i 牌用来凑顺子,如果凑不出来,就说明失败了
注意这些就可以在 O ( n 3 ) \mathcal{O}(n^3) O(n3) 下跑过
AC-code:
#include<bits/stdc++.h>
using namespace std;
int rd() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * w;
}
void wt(int x) {
static int sta[35];
int f = 1;
if(x < 0) f = -1,x *= f;
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
if(f == -1) putchar('-');
while (top) putchar(sta[--top] + 48);
}
int n,m,card[405];
namespace workplace{
int a[405];
bool check() {
for(int i = 1;i<=n;i++) {
if(card[i] >= 2) {
card[i] -= 2;
bool ok = true;
for(int j = 1;j<=n + 2;j++) a[j] = card[j];
for(int j = 1;j<=n + 2;j++) {
if(a[j] < 0) {
ok = false;
break;
}
a[j] %= 3;
a[j + 1] -= a[j];
a[j + 2] -= a[j];
}
card[i] += 2;
if(ok) return true;
}
}
return false;
}
void solve() {
bool non = 0,re = false;
for(int i = 1;i<=n;i++) {
card[i]++;
non |= (re = check());
if(re)
wt(i),putchar(' ');
card[i]--;
}
if(!non) puts("NO");
}
}
signed main() {
n = rd(),m = rd();
for(int i = 1;i<=3 * m + 1;i++) card[rd()]++;
workplace::solve();
return 0;
}