UVa11210回溯法递归

该博客介绍了如何利用回溯法解决UVa11210题目,即在已有13张麻将牌的基础上,通过遍历所有可能摸到的第14张牌,判断能否胡牌。博主分享了关键思路,包括将牌转化为易于处理的形式,寻找将、顺子和刻子的递归方法,以及在回溯过程中需要注意的细节。提供了完整的代码示例以供参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:点击这里

解题思路

对于不懂麻将的我来说,此题花了不少时间去揣摩题意。不过其实题目简化了规则:手上有13张牌,要摸一张胡牌,叫你求摸哪一张可以胡牌。由于整副牌只能拆成一个将(两张相同的牌),不限数量个刻子或顺子。因此,我们可以想象,任意摸一张牌后,除去将,还有12张牌,正好分为4组(3张为一组)刻子或顺子。
因此,此题首先应该是遍历所有可能摸到的牌,然后检查当前牌是否可能胡牌。检查的方式应该为先找将(有两张以上的牌都可能),然后递归找顺子和刻子。

技巧及注意事项

  1. 此题思路比较容易,但第一步是要把所有的字符串转换为便于处理和表达的字符。因此本文直接将其转换为了ID。
  2. 寻找顺子时,从顺子的最小值开始取,因此最大顺子为7W8W9W,并且注意在连续的T和S和W之间是不能形成顺子的。
  3. 回溯法要注意每次进行递归尝试后,要将减去的数量重新加上。
  4. 注意在主函数中,若找到和牌,number中的数量会变为0,因此需要额外数组进行还原。
  5. 在添加第14张牌时,若手牌种相同的牌已经有4张,则不可能再添加。

代码示例:

#include <iostream>
#include <string>
#include <string.h>

using namespace std;

const string mahjong[] =
{
    "1T","2T","3T","4T","5T","6T","7T","8T","9T",
    "1S","2S","3S","4S","5S","6S","7S","8S","9S",
    "1W","2W","3W","4W","5W","6W","7W","8W","9W",
    "DONG","NAN","XI","BEI",
    "ZHONG","FA","BAI"
};

int convert(string name)
{
    for (int i = 0; i < 34; i++)
    {
        if (mahjong[i] == name) return i;
    }
    return -1;
}

int number[34],origin[34];

bool recursive(int dep)
{
    int i;
    for (i = 0; i < 34; i++)
    {
        if (number[i] >= 3)
        {
            //可以为刻子,三张相同的牌
            if (dep == 3) //每次无论是顺子还是刻子,只有4组
            {
                return true;
            }
            number[i] -= 3; //用过了这三张牌
            if (recursive(dep+1))
            {
                return true;
            }
            number[i] +=3;
        }
    }
    for (i = 0; i < 24; i++)
    {
        //要成为顺子,最大的牌为7W
        if (i%9 <= 6 && number[i] >=1 && number[i+1] >=1 && number[i+2] >=1 )
        {
            //可以为顺子
            if (dep == 3) return true;//四组都找到了
            number[i]--;
            number[i+1]--;
            number[i+2]--;
            if (recursive(dep+1))
            {
                return true;
            }
            number[i]++;
            number[i+1]++;
            number[i+2]++;
        }

    }
    return false;
}


bool check()
{
    for (int i = 0 ; i < 34; i++)
    {
        if (number[i] >= 2)
        {
            //先找将牌
            number[i] -=2;
            if (recursive(0))
                //可以和牌
                return true;
            number[i] +=2;
        }
    }
    return false;
}


int main()
{
    int kcase = 0,i,j;
    bool hu;
    string temp;
    while (cin>>temp)
    {
        if (temp[0] == '0')
            break;
        cout<<"Case "<<++kcase<<":";
        memset(number,0,sizeof(number));
        number[convert(temp)]++;
        for (i = 1; i < 13; i++)
        {
            cin>>temp;
            number[convert(temp)]++;
        }
        hu = false;
        memcpy(origin,number,sizeof(origin));
        for (i = 0 ;i < 34; i++)
        {

            if (number[i] >= 4)
                continue;//已经不能再多了
            number[i]++;
            if (check())
            {
                hu = true;
                cout<<" "<<mahjong[i];
                //如果胡牌,则number数组中的元素已经用完,需要重新赋值
                memcpy(number,origin,sizeof(number));
                continue;
            }
            //若没有胡牌,要减去当前新加进去的牌
            number[i]--;
        }
        if (!hu)
        {
            cout<<" Not ready";
        }
        cout<<"\n";

    }
    return 0;
}

最后,欢迎关注我的个人博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值