[ZJOI 2006]超级麻将

Description

Input

第一行一个整数N(N<=100),表示玩了N次超级麻将。 接下来N行,每行100个数a1..a100,描述每次玩牌手中各种牌的数量。ai表示数字为i的牌有ai张。(0<=ai<=100)

Output

输出N行,若胡了则输出Yes,否则输出No,注意区分Yes,No的大小写!

Sample Input

3
2 4 0 0 0 0 0 …… 0(一共98个0)
2 4 2 0 0 0 0 …… 0(一共97个0)
2 3 2 0 0 0 0 …… 0(一共97个0)

Sample Output

Yes
Yes
No

题解

这道题题解很多都是用贪心+$Hash$搜索做的,其实$DP$也可以解决这道题。

我们考虑选取麻将的先后是不互相影响的。且怎么选当前牌只会影响其相邻的几张牌,我们将这些影响的状态放入方程中,保证无后效性。

令: $f[i][j][k][0/1]$ 表示“择第 $i$ 号牌时,第 $i-1$ 号牌要打出 $j$ 张,第 $i$ 号牌要打出 $k$ 张,之前选的所有牌是否( $0/1$ )选择了将(对子)”是否可行。

于是就有转移方程:

  1. 考虑选这i号牌做将(对子):
    if (k>1) f[i][j][k][1]|=f[i][j][k-2][0];
  2. 考虑i号牌碰(三张相同):
    if (k>2) f[i][j][k][1]|=f[i][j][k-3][1],f[i][j][k][0]|=f[i][j][k-3][0];
  3. 考虑i号牌杠(四张相同):
    if (k>3) f[i][j][k][1]|=f[i][j][k-4][1],f[i][j][k][0]|=f[i][j][k-4][0];
  4. 考虑i-2,i-1,i三张牌吃(三个连续数字):
    if (j>=k&&a[i-2]>=k) f[i][j][k][0]|=f[i-1][a[i-2]-k][j-k][0],f[i][j][k][1]|=f[i-1][a[i-2]-k][j-k][1];

最后结果为$f[100][a[99]][a[100]][1]$。

附的代码有个玄学的写法:当$i==1$时,$a[i-2]$越界?我也不知道它访问到哪去了,但$AC$了就苟活着吧。

人生处处是惊喜...难道不是吗?

 

 1 #include<set>
 2 #include<map>
 3 #include<ctime>
 4 #include<cmath>
 5 #include<queue>
 6 #include<stack>
 7 #include<cstdio>
 8 #include<string>
 9 #include<vector>
10 #include<cstring>
11 #include<cstdlib>
12 #include<iostream>
13 #include<algorithm>
14 #define LL long long
15 #define RE register
16 #define IL inline
17 using namespace std;
18 
19 int n,a[105];
20 bool f[105][105][105][2];
21 
22 int main()
23 {
24     scanf("%d",&n);
25     while (n--)
26     {
27         memset(f,0,sizeof(f));
28         for (RE int i=1;i<=100;i++) scanf("%d",&a[i]);
29         f[0][0][0][0]=1;
30         for (RE int i=1;i<=100;i++)
31             for (RE int j=0;j<=a[i-1];j++)
32                 for (RE int k=0;k<=a[i];k++)
33                 {
34                     if (k>1) f[i][j][k][1]|=f[i][j][k-2][0];
35                     if (k>2) f[i][j][k][1]|=f[i][j][k-3][1],f[i][j][k][0]|=f[i][j][k-3][0];
36                     if (k>3) f[i][j][k][1]|=f[i][j][k-4][1],f[i][j][k][0]|=f[i][j][k-4][0];
37                     if (j>=k&&a[i-2]>=k) f[i][j][k][0]|=f[i-1][a[i-2]-k][j-k][0],f[i][j][k][1]|=f[i-1][a[i-2]-k][j-k][1];
38                 }
39         printf(f[100][a[99]][a[100]][1] ? "Yes\n":"No\n");
40     }
41     return 0;
42 }

 

 

 

转载于:https://www.cnblogs.com/NaVi-Awson/p/7257368.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值