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做法,我得好好想想,不过在网上找不到

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

 #include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <ctime>
using namespace std;
typedef long long LL;
typedef double DB;
#define For(i, s, t) for(int i = (s); i <= (t); i++)
#define Ford(i, s, t) for(int i = (s); i >= (t); i--)
#define Rep(i, t) for(int i = (0); i < (t); i++)
#define Repn(i, t) for(int i = ((t)-1); i >= (0); i--)
#define rep(i, x, t) for(int i = (x); i < (t); i++)
#define MIT (2147483647)
#define INF (1000000001)
#define MLL (1000000000000000001LL)
#define sz(x) ((int) (x).size())
#define clr(x, y) memset(x, y, sizeof(x))
#define puf push_front
#define pub push_back
#define pof pop_front
#define pob pop_back
#define ft first
#define sd second
#define mk make_pair
inline void SetIO(string Name) {
string Input = Name+".in",
Output = Name+".out";
freopen(Input.c_str(), "r", stdin),
freopen(Output.c_str(), "w", stdout);
} const int N = ;
int n, m, Arr[N];
int Ans[N], Tot; inline void Input() {
scanf("%d%d", &n, &m);
For(i, , *m+) {
int x;
scanf("%d", &x);
Arr[x]++;
}
} int Tmp[N];
inline bool Check(int x) {
For(i, , n) {
For(j, , n) Tmp[j] = Arr[j];
Tmp[x]++;
Tmp[i] -= ;
if(Tmp[i] < ) continue; bool flag = ;
Ford(j, n, ) {
if(Tmp[j] < ) {
flag = ;
break;
}
if(!Tmp[j]) continue;
Tmp[j] %= ;
Tmp[j-] -= Tmp[j];
Tmp[j-] -= Tmp[j];
Tmp[j] = ;
}
Tmp[] %= , Tmp[] %= ;
if(Tmp[] || Tmp[]) flag = ; if(flag) return ;
}
return ;
} inline void Solve() {
For(i, , n)
if(Check(i)) Ans[++Tot] = i; if(!Tot) puts("NO");
else {
For(i, , Tot-) printf("%d ", Ans[i]);
printf("%d\n", Ans[Tot]);
}
} int main() {
#ifndef ONLINE_JUDGE
SetIO("");
#endif
Input();
Solve();
return ;
}

这是正着枚举

 #include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <ctime>
using namespace std;
typedef long long LL;
typedef double DB;
#define For(i, s, t) for(int i = (s); i <= (t); i++)
#define Ford(i, s, t) for(int i = (s); i >= (t); i--)
#define Rep(i, t) for(int i = (0); i < (t); i++)
#define Repn(i, t) for(int i = ((t)-1); i >= (0); i--)
#define rep(i, x, t) for(int i = (x); i < (t); i++)
#define MIT (2147483647)
#define INF (1000000001)
#define MLL (1000000000000000001LL)
#define sz(x) ((int) (x).size())
#define clr(x, y) memset(x, y, sizeof(x))
#define puf push_front
#define pub push_back
#define pof pop_front
#define pob pop_back
#define ft first
#define sd second
#define mk make_pair
inline void SetIO(string Name) {
string Input = Name+".in",
Output = Name+".out";
freopen(Input.c_str(), "r", stdin),
freopen(Output.c_str(), "w", stdout);
} const int N = ;
int n, m, Arr[N];
int Ans[N], Tot; inline void Input() {
scanf("%d%d", &n, &m);
For(i, , *m+) {
int x;
scanf("%d", &x);
Arr[x]++;
}
} int Tmp[N];
inline bool Check(int x) {
For(i, , n) {
For(j, , n+) Tmp[j] = Arr[j];
Tmp[x]++;
Tmp[i] -= ;
if(Tmp[i] < ) continue; bool flag = ;
For(j, , n+) {
if(Tmp[j] < ) {
flag = ;
break;
}
if(!Tmp[j]) continue;
Tmp[j] %= ;
Tmp[j+] -= Tmp[j];
Tmp[j+] -= Tmp[j];
Tmp[j] = ;
}
if(flag) return ;
}
return ;
} inline void Solve() {
For(i, , n)
if(Check(i)) Ans[++Tot] = i; if(!Tot) puts("NO");
else {
For(i, , Tot-) printf("%d ", Ans[i]);
printf("%d\n", Ans[Tot]);
}
} int main() {
#ifndef ONLINE_JUDGE
SetIO("");
#endif
Input();
Solve();
return ;
}

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

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

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

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

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

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

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

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

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

bzoj1028 [JSOI2007]麻将的更多相关文章

  1. bzoj千题计划118:bzoj1028: [JSOI2007]麻将

    http://www.lydsy.com/JudgeOnline/problem.php?id=1028 枚举等待牌 枚举对是哪个 判断 #include<cstdio> #include ...

  2. 【BZOJ1028】[JSOI2007]麻将(贪心)

    [BZOJ1028][JSOI2007]麻将(贪心) 题面 BZOJ 洛谷 题解 感觉好久没打过麻将了,似乎都快不会打了. 这个数据范围看着就觉得是\(O(n^2m)\). 那么就枚举听哪张牌,然后枚 ...

  3. BZOJ 1028: [JSOI2007]麻将 暴力

    1028: [JSOI2007]麻将 Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/prob ...

  4. BZOJ 1028 [JSOI2007]麻将

    1028: [JSOI2007]麻将 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1270  Solved: 576[Submit][Status][ ...

  5. 1028: [JSOI2007]麻将

    1028: [JSOI2007]麻将 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 2638  Solved: 1168[Submit][Status] ...

  6. [JSOI2007]麻将 模拟 BZOJ1028

    题目描述 麻将是中国传统的娱乐工具之一.麻将牌的牌可以分为字牌(共有东.南.西.北.中.发.白七种)和序数牌(分为条子.饼子.万子三种花色,每种花色各有一到九的九种牌),每种牌各四张. 在麻将中,通常 ...

  7. 【bzoj1028】[JSOI2007]麻将

    首先枚举等待牌,再枚举对子牌.   然后1~n扫一遍,如果现在 s[i]不能被3整除,那么必须跟后两个数搭配几下变成能被3整除的.然后如果能被3整除,那么只要三个连续的一组可行,则三个相同的一组必定也 ...

  8. 1028: [JSOI2007]麻将 - BZOJ

    Description 麻将是中国传统的娱乐工具之一.麻将牌的牌可以分为字牌(共有东.南.西.北.中.发.白七种)和序数牌(分为条子.饼子.万子三种花色,每种花色各有一到九的九种牌),每种牌各四张.在 ...

  9. [JSOI2007]麻将

    Description 麻将是中国传统的娱乐工具之一.麻将牌的牌可以分为字牌(共有东.南.西.北.中.发.白七种)和序数 牌(分为条子.饼子.万子三种花色,每种花色各有一到九的九种牌),每种牌各四张. ...

随机推荐

  1. ThinkPHP 分页实现

    TP3.2框架手册,有一个数据分页,不过每次都要写太多的代码,还有中文设置等有些麻烦,做为程序开发者,有必要整理下: O.先看效果图 一.分页方法 /** * TODO 基础分页的相同代码封装,使前台 ...

  2. [Effective JavaScript 笔记] 第2条:理解JavaScript的浮点数

    JavaScript数值型类型只有数字 js只有一种数值型数据类型,不管是整数还是浮点数,js都把归为数字. typeof 17;   // “number” typeof 98.6; // “num ...

  3. 用Lucene检索数据库

    http://blog.sina.com.cn/s/blog_82ac67c101012r9z.html package com.javabean; import java.io.File;impor ...

  4. 2015安徽省赛 F.多重部分和问题

    题目描述 有n种不同大小的数字,每种各个.判断是否可以从这些数字之中选出若干使它们的和恰好为K. 输入 首先是一个正整数T(1<=T<=100) 接下来是T组数据 每组数据第一行是一个正整 ...

  5. C++中new的解说

    new int;//开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针) new int(100);//开辟一个存放整数的空间,并指定该整数的初值为100,返回一个指向该存储空间的地址 ...

  6. Minimum Size Subarray Sum

    Given an array of n positive integers and a positive integer s, find the minimal length of a subarra ...

  7. sharepoint获取域名和当前登录的应为名字

    string a =  SPContext.Current.Web.CurrentUser.ToString(); int length = a.IndexOf("w|", 0) ...

  8. Scanner 和 String 类的常用方法

    Scanner类是在jdk1.5 之后有了这个: 常用格式是: Scanner sc = new Scanner(System.in); 从以下版本开始: 1.5 构造方法摘要 Scanner(Fil ...

  9. JAVA接口继承、抽象类等

    1.定义接口 package test.intefaces; public interface TestIntefaceA { void testA(); void testB(); void tes ...

  10. HDU 1087 Super Jumping! Jumping! Jumping! 最大递增子序列

    Super Jumping! Jumping! Jumping! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 ...