百科问答小站 logo
百科问答小站 font logo



一个关于麻将清一色的问题? 第1页

  

user avatar   xia-yang-7 网友的相关建议: 
      

我寻思着这数据量也不大,直接枚举就完事儿了。答案 。

每张牌出现的次数在 之间,于是只有 种不同的状态。

把每种状态看成图论里的一个点,若 状态可以由 状态添加一张牌得到,则添加一条有向边 。

那么,考虑13张牌的任意状态 ,若 没听,则任何从 出发能走到的状态都不满足集合 的要求。这样一遍bfs就可以了,本质上是一个状压dp。

唯一要注意的是纯空听算不算听牌的问题。我的程序第一版只写了和牌判定算法,然后从所有状态里挑出和牌状态,再从和牌状态任意去掉一张来倒推听牌状态。这样做会影响 时解的数量。

所有 的解(23个):

       111122223333444555 111222233334444555 111222333344445555 111222333444455556 122223333444555666 222233334444555666 222333344445555666 222333444455556666 222333444555566667 233334444555666777 333344445555666777 333444455556666777 333444555566667777 333444555666677778 344445555666777888 444455556666777888 444555566667777888 444555666677778888 444555666777788889 455556666777888999 555566667777888999 555666677778888999 555666777788889999     

所有的X值对应的解的数量(假定纯空听视为听牌):

       13: 93600 14: 16044 15: 2797 16: 589 17: 145 18: 23     

附源码(C++20标准,g++ 10.2.0),欢迎帮忙debug:

       #include <bits/stdc++.h> using namespace std;  constexpr bool kAllowPureEmptyWait = true;  class ScopedInc {  public:   explicit ScopedInc(unsigned char& c, int v = 1) : c_(c), v_(v) { c += v; }   ~ScopedInc() { c_ -= v_; }  private:   unsigned char& c_;   int v_; };  class Hand {  public:   explicit Hand(int i) {     num_tiles_ = 0;     for (auto& v : tiles_) {       num_tiles_ += v = i % 5;       i /= 5;     }   }    int num_tiles() const { return num_tiles_; }    bool is_waiting() {     for (auto& v : tiles_) {       if (!kAllowPureEmptyWait && v == 4) continue;       ScopedInc s(v);       if (is_winning()) return true;     }     return false;   }      void print() const {     for (int i = 0; i < tiles_.size(); i++) {       for (int j = 0; j < tiles_[i]; j++) {         cout << i + 1;       }     }   }      vector<int> get_successors() {     vector<int> ret;     for (auto& v : tiles_) {       if (v == 4) continue;       ScopedInc s(v);       ret.push_back(encode());     }     return ret;   }   private:   using Arr = array<unsigned char, 9>;   using It = Arr::iterator;    bool is_winning() {     // Pair     for (auto& v : tiles_) {       if (v < 2) continue;       ScopedInc s(v, -2);       if (is_winning_without_pair(/*num_groups=*/0, tiles_.begin())) return true;     }     return false;   }      bool is_winning_without_pair(int num_groups, It start) {     if (num_groups == 4) return true;     for (It i = start; i != tiles_.end(); ++i) {       // Identical group       if (*i >= 3) {         ScopedInc s(*i, -3);         if (is_winning_without_pair(num_groups + 1, i)) return true;       }       // Sequential group       if (tiles_.end() - i >= 3 && i[0] > 0 && i[1] > 0 && i[2] > 0) {         ScopedInc s0(i[0], -1), s1(i[1], -1), s2(i[2], -1);         if (is_winning_without_pair(num_groups + 1, i)) return true;       }     }     return false;   }    int encode() const {     int ret = 0;     for (auto v : tiles_ | views::reverse) {       ret = ret * 5 + v;     }     return ret;   }      Arr tiles_;   int num_tiles_; };  int main() {   constexpr int N = 5*5*5*5*5*5*5*5*5;   queue<int> q;   vector<int> failure_source(N);   for (int i = 0; i < N; i++) {     Hand hand(i);     if (hand.num_tiles() == 13 && !hand.is_waiting()) {       q.push(i);       failure_source[i] = i;     }   }   vector<bool> ok(N, true);   while (!q.empty()) {     Hand hand(q.front());     for (int successor : hand.get_successors()) {       if (ok[successor]) {         q.push(successor);         ok[successor] = false;         failure_source[successor] = failure_source[q.front()];       }     }     q.pop();   }   int best_hand_v = -1, max_tiles = 0;   for (int i = 0; i < N; i++) {     int num_tiles = Hand(i).num_tiles();     if (num_tiles < 13) continue;     if (ok[i] && num_tiles > max_tiles) {       best_hand_v = i;       max_tiles = num_tiles;     }   }   cout << "Best max tiles: " << max_tiles << endl;   map<int, int> counts;   for (int i = 0; i < N; i++) {     Hand hand(i);     int num_tiles = hand.num_tiles();     if (num_tiles < 13 || !ok[i]) continue;     ++counts[num_tiles];     if (num_tiles == max_tiles) {       hand.print();       cout << endl;     }   }   cout << "# of solutions by # of tiles: " << endl;   for (auto [num_tiles, count] : counts) {     cout << num_tiles << ": " << count << endl;   }      return 0; }      


user avatar   chickers 网友的相关建议: 
      

A(18)={333444455556666777}.

已手动枚举过是成立的。再增加一个3或者7,存在3333444466667的反例。再增加3个8,存在3345556677788的反例。

补充一下枚举过程:首先至少有1枚3和1枚7,否则转化为4种手牌的情况,已被火警证明过。为节省计算量,仅列出每种数牌的枚数以及其中1种听牌,如3444555566667记作13441,听3。

①1枚3+1枚7:

13441→1333,听3

14341→313,听5

②1枚3+2枚7:

12442→1342→232,听7

13342→2242→22,听7

13432→2332,听7

14242→1102,听2

14332→232,听7

14422→22,听7

③1枚3+3枚7:

11443→1111,听3

12343→124,听7

12433→124,听3

13243→214,听4

13333,听3

13423→232,听4

14233→142,听3

14323→3223,听5

14413→1111,听3

④2枚3+2枚7:

21442→21112:听7

22342→142,听5

22432→232,听7

23242→2002,听3

23332,听3

⑤2枚3+3枚7:

20443→2011,听4

21343→2101,听5

21433→214,听3

22243→2221,听3

22333,听3

22423→1102,听2

23143→2011,听4

23233,听3

23323,听3

23413→2011,听4

24043→2101,听5

24133→211,听3

24223→2122,听7

24313→2101,听5

24403→211,听3

⑥3枚3+3枚7:

30343→313,听4

30433→301,听4

31243→121,听5

31333,听4

31423→112,听3

32143→214,听4

32233,听4

32323,听4

32413→211,听4

33043→13,听5

33133,听5

枚举完毕,故X≥18.


user avatar   nan-hai-xiang-71 网友的相关建议: 
      

追更:

仍然先从结论来说:当集合A中包含四个连刻时,X =18。(刻子是三张相同的牌组成的面子,四连刻是指四个连续的刻子。例如:333444555666)

这里给出三个X=18的例子:A(18)={233334444555666777},{222333444455556666},{33344455556666777}。且他们平移或左右翻折后仍然可行。

具体求解过程用了递推和枚举遍历,由于过于复杂,就不做说明了。

原答案:

(题目经过一次修改,原题目为:麻将中同一花色的牌有 36 张,在 36 中选取一个整数值 x,13≤x≤36,那么这个整数 x 是否存在一个最大值 X,从 36 张一色牌中任意取出 X 张牌形成集合 A,使得手牌在门清状态下任意从 A 中选取 13 张手牌,都能形成一般形的清一色听牌,如果存在,这个最大值 X 是多少,请举出一个集合 A 的例子。)

从结论来说,不存在这样的X,使X满足题目中的条件。(后附问题修改建议)

1)证明:

首先找到一个13张牌的集合P,使P不听牌,不妨令P={1133445667799}。

假设,存在一个整数x [13,36],从36张牌中“任取”x张牌,使得这x张牌组成的集合A中的“任意”13张牌都是听牌。

既然是“任取”,那不妨令A P。

于是A中则有一种13张牌的组合P不是听牌,与假设矛盾。

故,不存在这样的x,使x满足题目中的条件。

2)问题修改建议:

麻将中同一花色的牌有 36 张,在 36 中选取一个整数值 x,13≤x≤36,那么这个整数 x 是否存在一个最大值 X,从 36 张一色牌中【存在一种】 X 张牌形成集合 A,使得手牌在门清状态下任意从 A 中选取 13 张手牌,都能形成一般形的清一色听牌,如果存在,这个最大值 X 是多少,请举出一个集合 A 的例子。


user avatar   dight119 网友的相关建议: 
      

抛砖引玉一下,盲猜 X = 16

A(16) = {3333444455556666}

分情况讨论如下:

A(无33) = {33444455556666} = {33, 444, 555, 666, 456} → 和牌

A(无44) = {33334455556666} = {333, 345, 456, 55, 666} → 和牌

A(无34) = {33344455556666} = {333, 44, 456, 555, 666} → 和牌

A(无35) = {33344445556666} = {333, 444, 456, 55, 666} → 和牌

A(无36) = {33344445555666} = {333, 444, 456, 555, 66} → 和牌

A(无45) = {33334445556666} = {333, 345, 456, 456, 66} → 和牌

因为所有包含于 A(16) 的 A(14) 都是和牌,所以任意包含于 A(16) 的 A(13) 都是听牌。




  

相关话题

  1×0=0 是因为 0 乘以任何数字都等于 0,还是因为 1 乘任何数字都等于那个数? 
  如何看待 Google 2004 年在硅谷公路旁一巨型广告牌上贴出的那道数学题用于招聘? 
  如果在一个密闭空间里给一个普通人1亿年的时间,他可以推演出现在的数学定理吗? 
  对于数学研究生来说,(在你的方向)「打基础」到什么地步可以开始做研究了? 
  孩子今年六年级,数学一塌糊涂,作为家长该怎么帮助她提升成绩? 
  这道怎么求极限? 
  数学严密性如何影响科学? 
  研究数学花钱多吗? 
  如何用初等数学证明2的a次方(a大于零)大于1? 
  有哪些直观的现象,最终被数学证明是错误的? 

前一个讨论
麻将想从熟悉到精通,网上有哪些高手主播可以看?
下一个讨论
雀魂如何入门?





© 2024-11-21 - tinynew.org. All Rights Reserved.
© 2024-11-21 - tinynew.org. 保留所有权利