zoukankan      html  css  js  c++  java
  • codevs2464超级麻将

    题目链接http://codevs.cn/problem/2464/

    题目描述 Description

    很多人都知道玩麻将,当然也有人不知道,呵呵,不要紧,我在这里简要地介绍一下麻将规则:
    普通麻将有砣、索、万三种类型的牌,每种牌有 1~9个数字,其中相同的牌
    每个有四张,例如 1 砣~9 砣,1索~9 索,1 万~9 万各有 4 张,所以共 36*3=108张牌。胡牌时每人有 14 张牌,其中只要某人手里有若干句话(就是同种类型的牌连续三张或同种牌三张) ,另外再加上一对,即可胡牌。当然如果全是对,叫七小对,也可以胡牌。下图是连三张示例。

    四万 五万 六万

    四万 四万 四万

    要判断某人是否胡牌,显然一个弱智的算法就行了,某中学信息学小组超级麻将迷想了想,决定将普通麻将改造成超级麻将。
    所谓超级麻将没有了砣、索、万的区分,每种牌上的数字可以是 1~100,而每种数字的牌各有100 张。另外特别自由的是,玩牌的人手里想拿多少张牌都可以,好刺激哦!
    刺激归刺激,但是拿多了怎么胡牌呢?
    超级麻将规定只要一个人手里拿的牌是若干句话(三个连续数字的牌各一张组成一句话,三张或者四张同样数字的牌也算一句话) ,再加上一对相同的牌,就算胡了。
    作为信息学竞赛选手的你,麻烦你给这位超级麻将迷编个程序,判断能否胡牌。

    输入描述 Input Description

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

    输出描述 Output Description

       输出 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

    感想:

    怎么样,刚看到题目的你是不是一辆懵逼?不知所措?其实啊,我一开始也是这样(写了一个dfs,结果0分)后来在自(da)己(shen)的(de)思(bang)考(zhu)下,在做出这道题。

    题解:

    首先,我们要仔细读题,发现有如下这几种操作:

    1. 一种牌的个数-3
    2. 一种牌的个数-4
    3. 连续3种牌的个数-1
    4. 连续3种牌的个数-2

    也许你很奇怪,不是可以再继续三种牌减下去吗(如:-4,-5)?但有没有发现三种牌-4相当于每种牌都用了一次操作2,-5相当于每种牌都用了一次操作2有用了一次操作3!以此类推,我们可以发现,一共只有如上这4种操作。

    于是,我们可以先处理出一个ok数组,ok[i][j]表示i-j的差能否用操作1和操作2完成(相当于一个背包)

    接下来,就是激动人心的DP啦!

    f[i][j][k]代表到第i种牌为止,有j张i-1种牌,k张i种牌是否可行(即判断f[i][j][k]是否为true)

    而此时,我们会发现,还要处理多出来的两张牌的情况!!!!!!!!!!!!!!这是一个棘手的问题,但我们可以在数组后面多开一维l,表示当前是否已经出现有两张的牌的情况(即l=1或l=0)。

    然后,我们就可以利用ok数组和我们上面推出来的操作进行DP啦!

    代码

    #include <cstdio>
    #include <cstring>
    using namespace std;
    int i,j,k,n,m,x,y,t,T,a[105],p;
    bool dp[105][105][105][2],ok[105][3];
    void read(int &d){
    d=0;int f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){d=d*10+ch-'0';ch=getchar();}
    d=d*f;
    }
    int main(){
    memset(ok,0,sizeof ok);
    for (i=0;i<=100;i++)
    for (j=0;j<=2;j++)
    for (k=0;k<=(i-j)/3;k++)
    if ((i-j-3*k)%4==0)ok[i][j]=1;
    read(T);
    while (T--){
    for (i=1;i<=100;i++)read(a[i]);
    memset(dp,0,sizeof dp);
    dp[2][a[1]][a[2]][0]=1;
    for (i=2;i<=99;i++)
    for (j=0;j<=a[i-1];j++)
    for (k=0;k<=a[i];k++){
    for (p=0;p<=1;p++){
    if (ok[j][0])dp[i+1][k][a[i+1]][p]|=dp[i][j][k][p];
    if (ok[j][1]&&a[i+1]>0&&k>0)dp[i+1][k-1][a[i+1]-1][p]|=dp[i][j][k][p];
    if (ok[j][2]&&a[i+1]>1&&k>1)dp[i+1][k-2][a[i+1]-2][p]|=dp[i][j][k][p];
    }
    if (j>=2){
    if (ok[j-2][0])dp[i+1][k][a[i+1]][1]|=dp[i][j][k][0];
    if (ok[j-2][1]&&k>0&&a[i+1]>0)dp[i+1][k-1][a[i+1]-1][1]|=dp[i][j][k][0];
    if (ok[j-2][2]&&k>1&&a[i+1]>1)dp[i+1][k-2][a[i+1]-2][1]|=dp[i][j][k][0];
    }
    }
    bool flag=false;
    for (i=0;i<=a[99];i++)if (ok[i][0])for (j=0;j<=a[100];j++)if (ok[j][0])flag|=dp[100][i][j][1];
    for (i=0;i<=a[99];i++)if (ok[i][0])for (j=0;j<=a[100];j++)if (ok[j-2][0]&&j>=2)flag|=dp[100][i][j][0];
    for (i=0;i<=a[99];i++)if (ok[i-2][0]&&i>=2)for (j=0;j<=a[100];j++)if (ok[j][0])flag|=dp[100][i][j][0];
    if (flag)printf("Yes
    ");else printf("No
    ");
    }
    return 0;
    }
    
  • 相关阅读:
    某公司面试的SQL题目
    列存储索引
    JList动态添加元素
    Java中堆、栈、常量池等概念解析
    JButton大小设置问题?
    JAVA中定时器的使用
    线性表和链表的区别
    JTable表头显示问题以及如何让某行选中
    JPanel如何设置背景图片
    关于Scanner调用nextInt()异常try后不能二次输入问题
  • 原文地址:https://www.cnblogs.com/Acheing/p/6786390.html
Copyright © 2011-2022 走看看