bzoj1028 [JSOI2007]麻将

1028: [JSOI2007]麻将

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 1337  Solved: 601
[Submit][Status][Discuss]

Description

麻将是中国传统的娱乐工具之一。麻将牌的牌可以分为字牌(共有东、南、西、北、中、发、白七种)和序数牌(分为条子、饼子、万子三种花色,每种花色各有一到九的九种牌),每种牌各四张。在麻将中,通常情况下一组和了的牌(即完成的牌)由十四张牌组成。十四张牌中的两张组成对子(即完全相同的两张牌),剩余的十二张组成三张一组的四组,每一组须为顺子(即同花色且序数相连的序数牌,例如条子的三、四、五)或者是刻子(即完全相同的三张牌)。一组听牌的牌是指一组十三张牌,且再加上某一张牌就可以组成和牌。那一张加上的牌可以称为等待牌。  在这里,我们考虑一种特殊的麻将。在这种特殊的麻将里,没有字牌,花色也只有一种。但是,序数不被限制在一到九的范围内,而是在1到n的范围内。同时,也没有每一种牌四张的限制。一组和了的牌由3m + 2张牌组成,其中两张组成对子,其余3m张组成三张一组的m组,每组须为顺子或刻子。现给出一组3m + 1张的牌,要求判断该组牌是否为听牌(即还差一张就可以和牌)。如果是的话,输出所有可能的等待牌。

Input

包含两行。第一行包含两个由空格隔开整数n, m (9<=n<=400, 4<=m<=1000)。第二行包含3m + 1个由空格隔开整数,每个数均在范围1到n之内。这些数代表要求判断听牌的牌的序数。

Output

输出为一行。如果该组牌为听牌,则输出所有的可能的等待牌的序数,数字之间用一个空格隔开。所有的序数必须按从小到大的顺序输出。如果该组牌不是听牌,则输出"NO"。

Sample Input

9 4
1 1 2 2 3 3 5 5 5 7 8 8 8

Sample Output

6 7 9

HINT

 

Source

 

题意:很好懂,前面麻将的介绍基本不用看

精简后的题意

刻子(即完全相同的三张牌     顺子(序数相连的牌,例如三、四、五)           对子(即完全相同的两张牌

序数在1到n的范围内。每一种牌张数无限制。

一组和牌由3m + 2张牌组成,其中两张组成对子,其余3m张组成三张一组的m组,每组须为顺子或刻子。

现给出一组3m + 1张的牌,要求判断该组牌是否为听牌(即还差一张就可以和牌)。如果是的话,输出所有可能的等待牌。

 

分析:首先看到 n<=400

那么我们会想到的是枚举要加进哪张牌(毕竟要输出的是每一种方案而不是方案数)

那加进之后如何检验。。。。。

我们这样想问题   ->   数字为i的牌只能与 i+1, i+2组成顺子,而不考虑与i-1,i-2组成顺子(即规定一个方向,以免重复和为了下面叙述方便)

那么数字为n-1,n的牌一定要是若干个刻子(在抽走了组成了顺子的牌之后)

那么显然至少有(a[n]%3)个顺子

若顺子的个数大于等于3,即3*k+x个i,i+1,i+2这样个顺子,那么可以当成k个i的刻子和k个i+1的刻子和k个i+2的刻子以及x个顺子(x<3)

所以可以证明倒着枚举数字,优先安排刻子的,其次安排顺子的贪心顺序是正确的

 

当然正着枚举也是一样的道理

 

听说这题有dp做法,我得好好想想,不过在网上找不到

 

 

这是倒着枚举的代码,其实正着枚举要简单写(其实两代码一样)

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <deque>
 6 #include <vector>
 7 #include <queue>
 8 #include <iostream>
 9 #include <algorithm>
10 #include <map>
11 #include <set>
12 #include <ctime>
13 using namespace std;
14 typedef long long LL;
15 typedef double DB;
16 #define For(i, s, t) for(int i = (s); i <= (t); i++)
17 #define Ford(i, s, t) for(int i = (s); i >= (t); i--)
18 #define Rep(i, t) for(int i = (0); i < (t); i++)
19 #define Repn(i, t) for(int i = ((t)-1); i >= (0); i--)
20 #define rep(i, x, t) for(int i = (x); i < (t); i++)
21 #define MIT (2147483647)
22 #define INF (1000000001)
23 #define MLL (1000000000000000001LL)
24 #define sz(x) ((int) (x).size())
25 #define clr(x, y) memset(x, y, sizeof(x))
26 #define puf push_front
27 #define pub push_back
28 #define pof pop_front
29 #define pob pop_back
30 #define ft first
31 #define sd second
32 #define mk make_pair
33 inline void SetIO(string Name) {
34     string Input = Name+".in",
35     Output = Name+".out";
36     freopen(Input.c_str(), "r", stdin),
37     freopen(Output.c_str(), "w", stdout);
38 }
39 
40 const int N = 410;
41 int n, m, Arr[N];
42 int Ans[N], Tot;
43 
44 inline void Input() {
45     scanf("%d%d", &n, &m);
46     For(i, 1, 3*m+1) {
47         int x;
48         scanf("%d", &x);
49         Arr[x]++;
50     }
51 }
52 
53 int Tmp[N];
54 inline bool Check(int x) {
55     For(i, 1, n) {
56         For(j, 1, n) Tmp[j] = Arr[j];
57         Tmp[x]++;
58         Tmp[i] -= 2;
59         if(Tmp[i] < 0) continue;
60         
61         bool flag = 1;
62         Ford(j, n, 3) {
63             if(Tmp[j] < 0) {
64                 flag = 0;
65                 break;
66             }
67             if(!Tmp[j]) continue;
68             Tmp[j] %= 3;
69             Tmp[j-1] -= Tmp[j];
70             Tmp[j-2] -= Tmp[j];
71             Tmp[j] = 0;
72         }
73         Tmp[1] %= 3, Tmp[2] %= 3;
74         if(Tmp[1] || Tmp[2]) flag = 0;
75         
76         if(flag)  return 1;
77     }
78     return 0;
79 }
80 
81 inline void Solve() {
82     For(i, 1, n)
83         if(Check(i)) Ans[++Tot] = i;
84     
85     if(!Tot) puts("NO");
86     else {
87         For(i, 1, Tot-1) printf("%d ", Ans[i]);
88         printf("%d\n", Ans[Tot]);
89     }
90 }
91 
92 int main() {
93     #ifndef ONLINE_JUDGE 
94     SetIO("1028");
95     #endif 
96     Input();
97     Solve();
98     return 0;
99 }
View Code

 

 

这是正着枚举

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <deque>
 6 #include <vector>
 7 #include <queue>
 8 #include <iostream>
 9 #include <algorithm>
10 #include <map>
11 #include <set>
12 #include <ctime>
13 using namespace std;
14 typedef long long LL;
15 typedef double DB;
16 #define For(i, s, t) for(int i = (s); i <= (t); i++)
17 #define Ford(i, s, t) for(int i = (s); i >= (t); i--)
18 #define Rep(i, t) for(int i = (0); i < (t); i++)
19 #define Repn(i, t) for(int i = ((t)-1); i >= (0); i--)
20 #define rep(i, x, t) for(int i = (x); i < (t); i++)
21 #define MIT (2147483647)
22 #define INF (1000000001)
23 #define MLL (1000000000000000001LL)
24 #define sz(x) ((int) (x).size())
25 #define clr(x, y) memset(x, y, sizeof(x))
26 #define puf push_front
27 #define pub push_back
28 #define pof pop_front
29 #define pob pop_back
30 #define ft first
31 #define sd second
32 #define mk make_pair
33 inline void SetIO(string Name) {
34     string Input = Name+".in",
35     Output = Name+".out";
36     freopen(Input.c_str(), "r", stdin),
37     freopen(Output.c_str(), "w", stdout);
38 }
39 
40 const int N = 410;
41 int n, m, Arr[N];
42 int Ans[N], Tot;
43 
44 inline void Input() {
45     scanf("%d%d", &n, &m);
46     For(i, 1, 3*m+1) {
47         int x;
48         scanf("%d", &x);
49         Arr[x]++;
50     }
51 }
52 
53 int Tmp[N];
54 inline bool Check(int x) {
55     For(i, 1, n) {
56         For(j, 1, n+2) Tmp[j] = Arr[j];
57         Tmp[x]++;
58         Tmp[i] -= 2;
59         if(Tmp[i] < 0) continue;
60         
61         bool flag = 1;
62         For(j, 1, n+2) {
63             if(Tmp[j] < 0) {
64                 flag = 0;
65                 break;
66             }
67             if(!Tmp[j]) continue;
68             Tmp[j] %= 3;
69             Tmp[j+1] -= Tmp[j];
70             Tmp[j+2] -= Tmp[j];
71             Tmp[j] = 0;
72         }
73         if(flag)  return 1;
74     }
75     return 0;
76 }
77 
78 inline void Solve() {
79     For(i, 1, n)
80         if(Check(i)) Ans[++Tot] = i;
81     
82     if(!Tot) puts("NO");
83     else {
84         For(i, 1, Tot-1) printf("%d ", Ans[i]);
85         printf("%d\n", Ans[Tot]);
86     }
87 }
88 
89 int main() {
90     #ifndef ONLINE_JUDGE 
91     SetIO("1028");
92     #endif 
93     Input();
94     Solve();
95     return 0;
96 }
View Code

 

 

 

//-------------------------------------------------------

大概是想出来如何dp了,不过比较麻烦,但复杂度较低

先是枚举加入的数字,然后dp求解

dp[2][400][1000][1000][1000]

第一位表示是否选择了对子,第二位表示当前进行到了第个数字(设为第x个数字),第三位-第五位表示从第x,x+1,x+2个数字的个数

显然有效状态很少,我们可以用队列和Hash实现这个dp过程

转移时对于每一个状态的转移参考那个优先刻子,其次顺子的贪心策略,推到下一个状态

整个过程的复杂度应该是O(n^2)

 

听说还有O(n)的dp做法,我再想想

posted @ 2015-09-02 21:19  yanzx6  阅读(177)  评论(0编辑  收藏  举报