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 <stdio.h>
#include <string.h>

#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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章