《算法竞赛-训练指南》第一章-1.9——UVa11210

这是个中国麻将的问题,也是道比较麻烦的模拟题目,自己如果没有看题解,私自认为还是写不出来的。

因为思想都没有,解决问题的办法也没有。根本就是一片空白,但看了题解,才豁然开朗,原来是这么回事!


其中,很重要的一点就是,将你所需要的题目中给出的事例打成表,然后转换成唯一的数字问题,这样找起来,对应起来就简单多了。

然后就是枚举,说实话,天天看别人枚举,枚举。以为是一种很简单的方法,但其实,枚举是一种很奇怪的思想,是一种计算机独有的思想,你要学会用计算机思考问题,而不是用人的智能化去想问题,那么你永远都做不会枚举的题目。

此题的步骤。找听牌,不如就给他所有种类的牌,然后判断这种状态是不是赢的状态。当然赢的状态很多种,但我们只要能找到一种,那么这张牌就是一张听牌。

要赢,就必须要有唯一的一对将,所以我们首先就要把将的问题解决,如果连将都没有,那肯定是赢不了的。

然后就是一个一个的去三个和顺子。当然这两个是互不影响的。而且在每次去的过程中,都要回溯递归求解。即不成功就恢复原来的数据。

好吧,这样做起来就基本上能作对了,但是,如果不小心,想的不周到,,这道题目还是很难A的。

贴出代码:

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

using namespace std;

char mahjong[44][22] = {"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 mj[22];

int c[44];

int converse(char *s) //将输入的字符串对应到每一种牌的id上 
{
	for (int i = 0; i < 34; i++)
	{
		if (strcmp(mahjong[i], s) == 0)
		{
			return i;
		}
	}
	return -1;
}

bool search(int dep)
{
	for (int i = 0; i < 34; i++)
	{
		if (c[i] >= 3)//找到三个. 
		{
			if (dep == 3) //如果前面的已经满足了三个,那么这个一满足,就可以直接返回true 
			{
				return true;
			}
			c[i] -= 3;
			if (search(dep + 1))//如果不满足,就继续递归调用 
			{
				return true;
			}
			c[i] += 3;
		}
	}
	for (int i = 0; i <= 24; i++) //开始的时候把找三个的和找顺子的在一起了,明天不可以.找三个是找三个,找顺子是找顺子,	 
	{							//两者丝毫不一样. 
			
		if (i % 9 <= 6 && c[i] >= 1 && c[i + 1] >= 1 && c[i + 2] >= 1) // 注意i%9的含义是找到对应一起的顺子然后小于6是为了满足对应一起. 
		{
			if (dep == 3)
			{
				return true;
			}
			c[i] -= 1;
			c[i + 1] -= 1;
			c[i + 2] -= 1;
			if (search(dep + 1))
			{
				return true;
			}
			c[i] += 1;
			c[i + 1] += 1;
			c[i + 2] += 1;
		}
	}
	return false;
}
			

bool check()
{
	for (int i = 0; i < 34; i++)
	{
		if (c[i] >= 2)
		{
			//这里是不应该处理4个的时候的,四个仍然可以拿两个来当将的;错死我了. 
			c[i] -= 2;
			if (search(0))
			{
				return true;
			}
			c[i] += 2;
		}
	}
	return false;
}

int main()
{
	char s[100];
	int cnt = 1;
	while (scanf("%s", s) != EOF)
	{
		if (s[0] == '0')//处理输入结束 
		{
			break;
		}
		mj[0] = converse(s);
		for (int i = 1; i <= 12; i++)
		{
			scanf("%s", s);
			mj[i] = converse(s);
		}
		printf("Case %d:", cnt++);
		int flag = 0;
		for (int i = 0; i < 34; i++)
		{
			memset(c, 0, sizeof(c)); // 因为每次的处理都把C给弄坏了,所以每一次单独求一下c 
			for (int j = 0; j < 13; j++)
			{
				c[mj[j]]++;
			}
			if (c[i] >= 4)
			{
				continue;
			}
			c[i]++;
			if (check())
			{
				printf(" %s", mahjong[i]);//这个处理最后输出结果的方法也不错,因为case 后面又个空格,所以一切的输出都可以这样了. 
				flag = 1;				//自己不用去可以控制格式了. 
			}
			c[i]--;
		}
		if (flag == 0)
		{
			printf(" Not ready");
		}
		printf("\n");
	}
			
//	system("pause");
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值