Google方程式

字符组成的等式:WWWDOT - GOOGLE = DOTCOM,

每个字符代表一个0-9之间的数字,WWWDOT、GOOGLE和DOTCOM都是合法的数字,不能以0开头。

请找出一组字符和数字的对应关系,使它们互相替换,并且替换后的数字能够满足等式。这个字符等式是Google公司能力倾向测试实验室的一道题目,这种题目主要考察人的逻辑推导能力和短期记忆能力,通常棋下的好的人解决这类问题会更得心应手一些(飞行棋例外)。

采用穷举递归的算法实现:

1.      建立数学模型

           要想让计算机解决问题,就要让计算机能够理解题目,这就需要建立一个计算机能够识别、处理的数学模型,首先要解决的问题就是建立字母和数字的映射关系的数学模型。本题的数学模型很简单,就是一个字母二元组:{char, number}。考察等式:

WWWDOT - GOOGLE = DOTCOM

共出现了9个不同的字母:W、D、O、T、G、L、E、C和M,因此,最终的解应该是9个字母对应的字母二元组向量:[ {'W', 7}, {'D', 5}, {'O', 8}, {'T', 9}, {'G', 1}, {'L', 0}, {'E', 3}, {'C', 4}, {'M', 6} ]。穷举算法就是对这个字母二元组向量中每个字母二元组的number元素进行穷举,number的穷举范围就是0-9共10个数字,当然,根据题目要求,有一些字符不能为0,比如W、G和D。排列组合问题的穷举多使用多重循环,看样子这个穷举算法应该是9重循环了,在每层循环中对一个字母进行从0到9遍历。问题是,必须这样吗,对于更通用的情况,不是9个字母的问题怎么办?首先思考一下是否每次都要遍历0-9。题目要求每个字母代表一个数字,而且不重复,很显然,对每个字母进行的并不是排列,而是某种形式的组合,举个例子,就是如果W字母占用了数字7,那么其它字母就肯定不是7,所以对D字母遍历是就可以跳过7。进一步,假设某次遍历的字母二元组向量中除M字母外其它8个字母已经有对应的数字了,比如:

[ {'W', 7}, {'D', 5}, {'O', 8}, {'T', 9}, {'G', 1}, {'L', 0}, {'E', 3}, {'C', 4}, {'M', ?} ] (序列-1)

那么M的可选范围就只有2和6,显然没必要使用9重循环。

现在换一种想法,对9个二元组的向量进行遍历,可以分解为两个步骤,首先确定第一个二元组的值,然后对剩下的8个二元组进行遍历。显然这是一种递归的思想(分治),算法很简单,但是要对10个数字的使用情况进行标识,对剩下的二元组进行遍历时只使用没有占用标识的数字。因此还需要一个标识数字占用情况的数字二元组定义,这个二元组可以这样定义:{number, using},0-9共有10个数字,因此需要维护一个长度为10的数字二元组向量。数字二元组向量的初始值是:

[{0, false}, {1, false},{2, false},{3, false},{4, false},{5, false},{6, false},{7, false},{8, false},{9, false}] 

每进行一重递归就有一个数字的using标志被置为true,当字母二元组向量得到(序列-1)的结果时,对应的数字二元组向量的值应该是:

[{0, true}, {1, true},{2, false},{3, true},{4, true},{5, true},{6, false},{7, true},{8, true},{9, true}] 

此时遍历这个数字二元组向量就可以知道M字母的可选值只能是2或6。

穷举遍历的结束条件是每层递归中遍历完所有using标志是false的数字,最外一层遍历完所有using标志是false的数字就结束了算法。

根据题目要求,开始位置的数字不能是0,也就是W、G和D这三个字母不能是0,这是一个“剪枝”条件,要利用起来,因此,对字母二元组进行扩充成字母三元组,添加一个leading标志:{char, number, leading}。下面就是这个数学模型的C语言定义:

typedef struct _CharItem {
char c;
int value;
BOOL leading;
}CharItem;


typedef struct _CharValue {
int value;
BOOL used;
}CharValue;

根据此数学模型初始化字母三元组和数字二元组 向量:

CharItem arrayItem[MAX_CHAR_COUNT] = {{'W', -1, TRUE}, 
{'D', -1, TRUE}, { 'G', -1, TRUE}, { 'O', -1, FALSE},  { 'T', -1, FALSE}, 
{ 'L', -1, FALSE},  { 'E', -1, FALSE}, { 'C', -1, FALSE},  { 'M', -1, FALSE}};
CharValue arrayVal[MAX_VALUE_COUNT] = {{0, FALSE}, 
{ 1, FALSE}, { 2, FALSE}, { 3, FALSE},  { 4, FALSE}, 
{ 5, FALSE},  {6, FALSE}, { 7, FALSE},  { 8, FALSE}, { 9, FALSE}};

2.      穷举算法

     建立数学模型,其实就是为了让计算机理解题目并处理相关的数据,算法就是告诉计算机如何使用这些模型中的数据。本文介绍的是穷举算法,算法的核心其实前面已经提到了,就是穷举所有的字母和数字的组合,对每种组合进行合法性判断,如果是合法的组合,就输出结果。

整个算法的核心是calCharItem()函数,其实这个函数简单:

/***************************************************
函数名: isValueUsed
说明: 递归调用遍历所有可能的情况
输入参数:CharItem arrayItem[]  字符《--》整数对应表
CharValue arrayVal   整数占用列表
int index 已经完成配对的字符
输出参数:无
返回值:  无
****************************************************/
void calCharItem(CharItem ci[MAX_CHAR_COUNT], 
CharValue cv[MAX_VALUE_COUNT], 
int index)
{
int i = 0;
if ( MAX_CHAR_COUNT == index )
{
//递归结束, 检测当前组合是否满足条件
marchValue(ci);
return;
}
for(i = 0;i < MAX_VALUE_COUNT; i++)
{
if ( !isValueUsed(ci[index], cv[i]) )
{
cv[i].used = TRUE;//设置已用标志
ci[index].value = cv[i].value;
calCharItem(ci, cv, index + 1);
cv[i].used = FALSE;//回溯, 清楚标志
}
}
}

代码

#include 
#include 

#define MAX_CHAR_COUNT	9
#define MAX_VALUE_COUNT	10

typedef unsigned char BOOL;
#define TRUE	1
#define FALSE	0

typedef struct _CharItem {
	char c;
	int value;
	BOOL leading;
}CharItem;

typedef struct _CharValue {
	int value;
	BOOL used;
}CharValue;

/***************************************************
函数名: getCharValue
说明: 返回字符对应的整数值
输入参数:CharItem arrayItem[]  字符《--》整数对应表
		 char ch  需要查询的字符
输出参数:无
返回值:  字符对应的整数
****************************************************/
int getCharValue(CharItem arrayItem[], char ch)
{
	int i = 0;

	for ( i = 0; i < MAX_CHAR_COUNT; i++)
	{
		if ( arrayItem[i].c == ch)
		{
			return arrayItem[i].value;
		}
	}

	return -1;
}

/***************************************************
函数名: calInt
说明: 计算字符串转换后的整数值
输入参数:CharItem arrayItem[]  字符《--》整数对应表
		 char p[]  需要计算的字符串
输出参数:无
返回值:  字符串转换后的整数
****************************************************/
int calInt(CharItem arrayItem[], char p[])
{
	int tmp = 0;
	int strCnt = 0;
	char *pTmp = NULL;

	if ( (NULL == arrayItem) || (NULL == p))
	{
		return -1;
	}

	strCnt = strlen(p);
	do 
	{
		tmp = tmp * 10 + getCharValue(arrayItem, *p);
		p++;
		strCnt--;
	} while ( 0 != strCnt);
	
	return tmp;
}

/***************************************************
函数名: marchValue
说明: 判断字符和整数的对应关系是否满足条件
      满足条件输出相应结果, 否则退出
输入参数:CharItem arrayItem[]  字符《--》整数对应表
输出参数:无
返回值:  无
****************************************************/
void marchValue(CharItem arrayItem[])
{
	//题目给定的求解字符串
	char *pCh1 = "WWWDOT";
	char *pCh2 = "GOOGLE";
	char *pCh3 = "DOTCOM";

	int num1 = calInt(arrayItem, pCh1);
	int num2 = calInt(arrayItem, pCh2);
	int num3 = calInt(arrayItem, pCh3);

	if ( (num1 - num2) == num3 )
	{
		printf("%s - %s = %s\n", pCh1, pCh2, pCh3);
		printf("%d - %d = %d\n", num1, num2, num3);
	}

}

/***************************************************
函数名: isValueUsed
说明: 判断当前数字是否被占用
输入参数:CharItem arrayItem[]  字符《--》整数对应表
		 CharValue arrayVal   整数占用列表
输出参数:无
返回值:  TRUE   被占用不可以使用
          FALSE 未被占用可以使用
****************************************************/
BOOL isValueUsed(CharItem arrayItem, CharValue arrayVal)
{
	if ( arrayVal.used )
	{
		return TRUE;
	}

	//首位字符不能为0
	if ( arrayItem.leading && (0 == arrayVal.value))
	{
		return TRUE;
	}

	return FALSE;
}

/***************************************************
函数名: isValueUsed
说明: 递归调用遍历所有可能的情况
输入参数:CharItem arrayItem[]  字符《--》整数对应表
		 CharValue arrayVal   整数占用列表
		 int index 已经完成配对的字符

输出参数:无
返回值:  无
****************************************************/
void calCharItem(CharItem ci[MAX_CHAR_COUNT], 
				 CharValue cv[MAX_VALUE_COUNT], 
				 int index)
{
	int i = 0;

	if ( MAX_CHAR_COUNT == index )
	{
		//递归结束, 检测当前组合是否满足条件
		marchValue(ci);
		return;
	}

	for(i = 0;i < MAX_VALUE_COUNT; i++)
	{
		if ( !isValueUsed(ci[index], cv[i]) )
		{
			cv[i].used = TRUE;//设置已用标志
			ci[index].value = cv[i].value;
			calCharItem(ci, cv, index + 1);
			cv[i].used = FALSE;//回溯, 清楚标志
		}
	}

}

int main()
{
	CharItem arrayItem[MAX_CHAR_COUNT] = {{'W', -1, TRUE}, 
			{'D', -1, TRUE}, { 'G', -1, TRUE}, { 'O', -1, FALSE},  { 'T', -1, FALSE}, 
			{ 'L', -1, FALSE},  { 'E', -1, FALSE}, { 'C', -1, FALSE},  { 'M', -1, FALSE}};
	CharValue arrayVal[MAX_VALUE_COUNT] = {{0, FALSE}, 
			{ 1, FALSE}, { 2, FALSE}, { 3, FALSE},  { 4, FALSE}, 
			{ 5, FALSE},  {6, FALSE}, { 7, FALSE},  { 8, FALSE}, { 9, FALSE}};

	calCharItem(arrayItem, arrayVal, 0);

	return 0;
}

http://blog.csdn.net/orbit/article/details/6529277

你可能感兴趣的:(C/C++)