zoukankan      html  css  js  c++  java
  • bzoj 1860: [Zjoi2006]Mahjong麻将 题解

    【原题】

    1860: [Zjoi2006]Mahjong麻将

    Time Limit: 1 Sec  Memory Limit: 64 MB
    Submit: 211  Solved: 122
    [Submit][Status]

    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

    HINT

    Source


    【分析1】原来记得也有一道麻将的判和问题(当然不是国家集训队的那道)。

    当时随便用贪心使过去了。可是这里3张牌和4张牌结合在了一起,于是贪心肯定挂了。然后就百思不得其解。

    瞄了一眼题解:原来是爆搜。然后開始自己YY。由于最多是三张连续的牌,所以假设1--K-1张牌都没了,第K张牌一定仅仅会影响到K+1和K+2。

    然后我就把K去爆搜。

    显然光是这样肯定T了。

    假设我把1--K-1以多种方法所有消掉了,第K种的状态就仅仅需搜一遍就可以。因此我依据K,K+1,K+2眼下的数量以及K的位置HASH一下。

    PS:用了凌神的64位自然溢出。

    【代码1】

    #include<cstdio>
    #include<set>
    using namespace std;
    typedef unsigned long long ULL;
    set<ULL>S;ULL base[101],a[101],mul=97;
    inline ULL calc(int k) {return base[k]*a[k]+base[k+1]*a[k+1]+base[k+2]*a[k+2];}
    int Test,i;
    inline bool dfs(int k,bool two)
    {
      ULL t=calc(k);
      if (S.find(t)!=S.end()) 
        return 0;
      S.insert(t);
      while (!a[k]) k++;
      if (k==101) return two;
      if (a[k]&&a[k+1]&&a[k+2])
      {
        a[k]--;a[k+1]--;a[k+2]--;
        if (dfs(k,two)) return 1;
        a[k]++;a[k+1]++;a[k+2]++;
      }
      if (a[k]>=4)
      {
        a[k]-=4;if (dfs(k,two)) return 1;a[k]+=4;
      }
      if (a[k]>=3)
      {
        a[k]-=3;if (dfs(k,two)) return 1;a[k]+=3;
      }
      if (a[k]>=2&&!two)
      {
        a[k]-=2;if (dfs(k,1)) return 1;a[k]+=2;
      }
      return 0;
    }
    int main()
    {
      base[1]=1;for (i=2;i<=100;i++) base[i]=base[i-1]*mul;
      scanf("%d",&Test);
      while (Test--)
      {
        for (i=1;i<=100;i++) scanf("%lld",&a[i]);
        S.clear();
        if (dfs(1,0)) puts("Yes");else puts("No");
      }
      return 0;
    }

    【分析2】上述做法尽管例子过了。可是狂WA不止。后来发现这种状态数太多了,HASH肯定会被卡掉。在WCY大神的指导下。我发现仅仅需把K+1~N的情况哈希一下,这样子状态数显然变小了。

    【代码】

    #include<cstdio>
    #include<set>
    using namespace std;
    typedef unsigned long long ULL;
    set<ULL>S;ULL base[105],a[105],sum,mul=131ll;
    int Test,i;
    inline bool dfs(int k,bool two,ULL status)
    {
      if (S.find(status)!=S.end()) return 0;
      S.insert(status);
      while (!a[k]&&k<=100) k++;
      if (k==101) return two;
      if (a[k]&&a[k+1]&&a[k+2]&&k<=98)
      {
        a[k]--;a[k+1]--;a[k+2]--;
        if (dfs(k,two,status-base[k]-base[k+1]-base[k+2])) return 1;
        a[k]++;a[k+1]++;a[k+2]++;
      }
      if (a[k]>=4)
      {
        a[k]-=4;if (dfs(k,two,status-base[k]*4)) return 1;a[k]+=4;
      }
      if (a[k]>=3)
      {
        a[k]-=3;if (dfs(k,two,status-base[k]*3)) return 1;a[k]+=3;
      }
      if (a[k]>=2&&!two)
      {
        a[k]-=2;if (dfs(k,1,status-base[k]*2-base[100])) return 1;a[k]+=2;
      }
      return 0;
    }
    int main()
    {
      base[1]=1ll;for (i=2;i<=100;i++) base[i]=base[i-1]*mul;
      scanf("%d",&Test);
      while (Test--)
      {
        sum=0;for (i=1;i<=100;i++) scanf("%lld",&a[i]),sum+=base[i]*a[i];
        S.clear();
        if (dfs(1,0,sum)) puts("Yes");else puts("No");
      }
      return 0;
    }

  • 相关阅读:
    linux下解压命令大全
    位运算的技巧
    逆元
    asp.net core 教程(五)
    day7--面向对象进阶(内含反射和item系列)
    day6--面向对象初识
    day6--二分查找法
    day6--递归函数
    day5--装饰器函数的信息打印,迭代器,生成器,列表推导式,内置函数
    python零散补充与总结
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7323296.html
Copyright © 2011-2022 走看看