uva 11210 - Chinese Mahjong(暴力搜索)

        这道题目是《算法竞赛入门经典训练指南》第1章算法设计基础的例9,看起来有点麻烦,最近开始学打麻将,比较好的理解了这道题的意思,之前一直都不能理解题意。

        有了一定的麻将胡牌基础,这道题很容易理解。胡牌规则很简单,一个将(两个一样),3个顺子(3个同花相连的牌)或刻子(3张相同的牌)即可胡牌。题目的要求是给13 张牌判断是否已经听牌,即再有一张某种花色牌即可胡牌。如果是,输出需要哪一种花色牌即可胡牌,如果不是输出not ready。

        算法的大致思路,首先枚举34种花色牌,表示有这种花色的牌可以胡牌(如果这种花色牌在13张中已经有4张,则跳过)。现在即有14张牌,对于这14张牌,再枚举34张种花色牌,表示有这种花色的两张牌则作为将,再对剩下12张牌进行操作。

        剩下的12张牌使用dfs,及当搜索4次时,若果都符合则可以听牌。剩下的12张牌,依旧是枚举34种花色牌,先找这种花色是否有至少3张牌,如果有,则构成顺子,再继续搜索。如果没有则判断这张牌和与它后面相连的2张牌能否构成刻子,若可以,则构成刻子,再继续搜索。如果不能,则表示没有听牌,则跳出dfs,继续枚举将牌。需要注意的是Dong,Nan,Xi,Bei,Zhong,Fa,Bai都不能构成刻子。

        存储方式则是先用一个二维字符数组存下所有的麻将花色,再用一维数组通过预处理13张牌保存下13张各种花色的牌各有多少张。

        注意:《算法竞赛入门经典训练指南》上给的程序个人感觉有点小问题。在枚举将牌和搜索顺子、刻子的两个函数里,每当成功找到时直接返回true但是并没有将之前减掉的牌数加回,这样容易造成只能找到一个能够听牌的花色,剩下的花色找不出来,所以在每个return true之前都需要添加上牌数加回的代码。


#include <iostream>
#include "stdio.h"
#include "string.h"
using namespace std;
int n[35];
char mj[35][6] = {"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"
                 };
void convert(char a[])
{
    int i;
    for(i = 0; i < 34; i++)
        if(!strcmp(mj[i], a))
            n[i]++;
}
bool sear(int tmp, int j, int k)
{
    int i;
    for(i = 0; i < 34; i++)
    {
        if(n[i] == 3)
        {
            if(tmp == 3)
                return true;
            n[i] -= 3;
            if(sear(tmp + 1, j, k))
            {
                n[i] += 3;
                return true;
            }
            else
                n[i] += 3;
        }
        if(i < 27 && i % 9 < 7 && n[i] > 0 && n[i + 1] > 0 && n[i + 2] > 0)
        {
            if(tmp == 3)
                return true;
            n[i]--;
            n[i + 1]--;
            n[i + 2]--;
            if(sear(tmp + 1, j, k))
            {
                n[i]++;
                n[i + 1]++;
                n[i + 2]++;
                return true;
            }
            else
            {
                n[i]++;
                n[i + 1]++;
                n[i + 2]++;
            }
        }
        if(n[i] > 0)
            return false;
    }
    return false;
}
bool jiang(int tmp)
{
    int i;
    for(i = 0; i < 34; i++)
    {
        if(n[i] > 1)
        {
            n[i] -= 2;
            if(sear(0, tmp, i))
            {
                n[i] += 2;
                return true;
            }
            else
                n[i] += 2;
        }
    }
    return false;
}
int main()
{
    int i, f, cas = 0;
    char a[6];
    while(~scanf("%s", a))
    {
        if(a[0] == '0')
            break;
        memset(n, 0, sizeof(n));
        convert(a);
        for(i = 1; i < 13; i++)
        {
            scanf("%s", a);
            convert(a);
        }
        f = 1;
        printf("Case %d:", ++cas);
        for(i = 0; i < 34; i++)
        {
            if(n[i] == 4)
                continue;
            n[i]++;
            if(jiang(i))
            {
                printf(" %s", mj[i]);
                f = 0;
            }
            n[i]--;
        }
        if(f)
            printf(" Not ready");
        printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值