1. 程式人生 > >窮舉搜尋:Google方程式

窮舉搜尋:Google方程式

有一個由字元組成的方程式:WWWDOT-GOOGLE=DOTCOM 每一個字母代表一個不同的數字,不能以0開頭。

使用窮舉法就是對每個字母用0~9的數字嘗試10次,由於沒一個字母代表不同的數字,如果考慮0開頭的情況,這樣的組合有10*9*8*7*6*5*4*3*2*1=3628800組合。

資料結構的定義上,要避免固定9個字元的方法,就需要定義一可變化的字元元素列表,每個字元包含3個屬性:

struct CharItem
{
    char c;         // 字母
    int value;      // 數值
    bool leading;   // 是否為開頭
};

由於這是一個組合問題,兩個字母不能被指定為相同的數字,這樣需要對每個數字做一個標示,當這個數字被佔用時,其他就不可以使用此數字,結構體為:

struct CharValue
{
    bool used;      // 是否被使用
    int value;      // 數值
};

窮舉演算法採用遞迴方式進行列舉,按照CharItem列表的順序,對每個字元進行數字遍歷,演算法實現如下:

/* 核心: 為字母三元組陣列ci分配數值二元組中的可能的數值組合,
  index標誌分配到的字母編號,從0開始 */
void SearchingResult(CharItem ci[max_char_count],
                     CharValue cv[max_number_count],
                     int index, CharListReadyFuncPtr callback)//為返回函式,當返回時呼叫此函式;
{
    //為所有字元分配完數值,若是解則列印,然後返回上一層
    if(index == max_char_count)
    {
        callback(ci);//檢查ci是否是符合等式的字元組合,若符合則列印
        return;
    }
    //每層遞迴要遍歷所有未使用的數字
    for(int i = 0; i < max_number_count; ++i)
    {
        // 檢查能否分配該數字,檢查數字是否使用、字母是否是開頭字母
        if(IsValueValid(ci[index], cv[i]))
        {
            //窮舉賦值
            cv[i].used = true;
            ci[index].value = cv[i].value;
            //繼續下層遞迴
            SearchingResult(ci, cv, index + 1, callback);
            //繼續同層遍歷
            cv[i].used = false;
        }
    }
    return;
}

根據題目要求W、G、D不能為0,對這三個字元進行剪枝操作,用I是valueVlid()作為評估函式,callback函式被呼叫的次數減少到30%,callback函式的編寫

void CharListReady(CharItem ci[max_char_count])
{
    char* minuend    = "WWWDOT";
    char* subtrahend = "GOOGLE";
    char* diff       = "DOTCOM";


    int m = MakeIntegerValue(ci, minuend);
    int s = MakeIntegerValue(ci, subtrahend);
    int d = MakeIntegerValue(ci, diff);
    if((m - s) == d)
    {
        std::cout << m << " - " << s << " = " << d << std::endl;
    }
    return;
}

/* 回撥函式指標(回撥判斷本次分配的字母數值組合是否滿足方程) */
typedef void (*CharListReadyFuncPtr)(CharItem ci[max_char_count]);//void CharListReady(CharItem ci[max_char_count])宣告這個函式的指標


/* 從字母三元組陣列ci中,找到字元c對應的指向三元組的指標 */
CharItem* GetCharItem(CharItem ci[max_char_count], char c)
{
    for(int i = 0; i < max_char_count; ++i)
    {
        if(ci[i].c == c)
        {
            return &ci[i];
        }
    }
    return NULL;
}


/* 判斷是否可以為ci中的字母分配cv中的數字 */
bool IsValueValid(CharItem ci, CharValue cv)
{
    if(cv.used)
    {
        return false;
    }
    if(ci.leading && (cv.value == 0))
    {
        return false;
    }
    return true;
}


/* 根據字母三元組ci的分配,計算字串chars對應的整數值 */
int MakeIntegerValue(CharItem ci[max_char_count], char* chars)
{
    assert(chars);//assert巨集的原型定義在<assert.h>中,其作用是,則終止程式執行給其一個引數,即一個斷言為真的表示式,若果斷言不為真則傳送一個錯誤資訊告訴斷言是什麼,以及失敗之後,程式終止
    int value = 0;
    char* p = chars;
    while(*p)
    {
        CharItem* char_item = GetCharItem(ci, *p);//得到了一個結構體的物件這樣可以通過->符號來取得值
        if(char_item == NULL)
        {
            return 0;
        }
        value = value* 10 + char_item->value;
        p++;
    }
    return value;
}

int main()
{
    /CharValue char_val[max_number_count] =
    {
        {false,0}, {false,1}, {false,2}, {false,3},
        {false,4}, {false,5}, {false,6}, {false,7},
        {false,8}, {false,9}
    };

CharItem char_item[max_char_count] =
    {
        {'W',-1,true}, {'D',-1,true}, {'O',-1,false},
        {'T',-1,false}, {'G',-1,true}, {'L',-1,false},
        {'E',-1,false}, {'C',-1,false}, {'M',-1,false}
    };

SearchingResult(char_item, char_val, 0, CharListReady);/
    return 0;
}