【BZOJ】【1028】【JSOI2007】麻将
暴力/模拟
$n\leq400$,嗯……这是一个很小的数据范围= =
判断一副牌是不是听牌并求出听什么牌太麻烦了,干脆我们直接判是不是胡牌好了~
枚举胡的是哪张牌,然后判一下加上这张牌后是否能胡。
算法框架很好写:
1 F(i,1,n){ 2 a[i]++; 3 if (check()) ans[++tot]=i; 4 a[i]--; 5 }
但是这个check怎么写让我很蛋疼……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 int n,m,a[N],b[N],ans[N],tot; 2 bool dfs(int x){ 3 if (x==n+1 && b[x]==0 && b[x+1]==0) return 1; 4 if (b[x]==0) return dfs(x+1); 5 if (b[x]<0) return 0; 6 bool ans=0; 7 for(int i=b[x];i>=0;i-=3){ 8 b[x+1]-=i; b[x+2]-=i; 9 if (dfs(x+1)) return 1; 10 b[x+1]+=i; b[x+2]+=i; 11 } 12 return 0; 13 } 14 bool check(){ 15 F(i,1,n) b[i]=a[i]; 16 F(i,1,n) 17 if (b[i]>=2){ 18 b[i]-=2; 19 if (dfs(1)) return 1; 20 b[i]+=2; 21 } 22 return 0; 23 }
其实大胆猜想一下,可以得到一个更优的方案:每种牌优先考虑成刻子,剩余的再考虑成顺子(Orz Hzwer)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 bool check(){ 2 bool flag; 3 F(i,1,n) 4 if (a[i]>=2){ 5 flag=1; 6 a[i]-=2; 7 F(i,1,n+2) b[i]=a[i]; 8 F(j,1,n+2){ 9 if (b[j]<0){flag=0;break;} 10 b[j]%=3; 11 b[j+1]-=b[j]; 12 b[j+2]-=b[j]; 13 } 14 a[i]+=2; 15 if (flag) return 1; 16 } 17 return 0; 18 }
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /************************************************************** 2 Problem: 1028 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:308 ms 7 Memory:1276 kb 8 ****************************************************************/ 9 10 //BZOJ 1028 11 #include<cstdio> 12 #include<cstring> 13 #include<cstdlib> 14 #include<iostream> 15 #include<algorithm> 16 #define rep(i,n) for(int i=0;i<n;++i) 17 #define F(i,j,n) for(int i=j;i<=n;++i) 18 #define D(i,j,n) for(int i=j;i>=n;--i) 19 using namespace std; 20 typedef long long LL; 21 inline int getint(){ 22 int r=1,v=0; char ch=getchar(); 23 for(;!isdigit(ch);ch=getchar()) if(ch=='-')r=-1; 24 for(; isdigit(ch);ch=getchar()) v=v*10+ch-'0'; 25 return r*v; 26 } 27 const int N=410; 28 /*******************template********************/ 29 int n,m,a[N],b[N],ans[N],tot; 30 bool check(){ 31 bool flag; 32 F(i,1,n) 33 if (a[i]>=2){ 34 flag=1; 35 a[i]-=2; 36 F(i,1,n+2) b[i]=a[i]; 37 F(j,1,n+2){ 38 if (b[j]<0){flag=0;break;} 39 b[j]%=3; 40 b[j+1]-=b[j]; 41 b[j+2]-=b[j]; 42 } 43 a[i]+=2; 44 if (flag) return 1; 45 } 46 return 0; 47 } 48 int main(){ 49 #ifndef ONLINE_JUDGE 50 freopen("1028.in","r",stdin); 51 // freopen("1028.out","w",stdout); 52 #endif 53 n=getint(); m=getint(); 54 F(i,1,3*m+1) a[getint()]++; 55 F(i,1,n){ 56 a[i]++; 57 if (check()) ans[++tot]=i; 58 a[i]--; 59 } 60 if (!tot){printf("NO");return 0;} 61 F(i,1,tot) {printf("%d",ans[i]); if(i!=tot) printf(" ");} 62 return 0; 63 }
1028: [JSOI2007]麻将
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 1158 Solved: 519
[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
1 1 2 2 3 3 5 5 5 7 8 8 8