算法學習筆記(三)問題的轉化與高精度運算

問題:設購票點沒有任何的零錢,票價50美元,現有m人手持50美元,n人手持100美元,求這樣m+n個人構成的隊伍有多少種排隊方法可以使得整個售票過程不中斷。


分析:對於這個問題,經過簡單的模擬可以發現,每個手持100的前面必須有一個手持50的,同樣如果有k個手持100的連續出現,則前面至少連續k次50。

       這樣一來,可以設手持50元的爲+1,手持100元的爲-1,設ai爲爲第i個人所對應的值,則問題轉化爲數組的部分和a1+a2+...+ak≥0,其中k≤m+n,爲了求這樣的數列的個數,需要使用組合數學的相關知識,這個問題可以使用現成的結論,第n個Catalan數,公式爲:

       wKioL1QjiALRhjQ6AAAX2RPgzAA066.jpg

       在運算時,要特別注意m<n的情況,在這種情況下,無法滿足要求。


算法實現:由於購票人數一般很多,這就帶來了高精度運算的問題,不再能使用傳統的運算方式。高精度運算的一種方法是把數據都轉化爲字符串,再對字符串定義運算。

   ①將數據轉化爲字符串

   將數據轉化爲字符串的算法是十分有效的,尤其在單片機的編程當中,一般的算法是將整數從高位到低位順次存入一個字符數組,這時字符數組所存的數據是反的,這時候需要再設定一個數組將其反過來,通過查閱資料,發現了一種比較號的算法,它的亮點是①充分利用i++的先運算後++特性簡化代碼,②省略對位數的判斷,而採用while(n)判斷所有位數是不是都已經取完,③在字符數組尾部賦0從而保證字符串在最後一個有效位後結束。代碼如下:

void NumToString(int n, char* s)
{
    int i,j,temp[8]; //臨時數組,先把數位存入其中,倒序
    if(n == 0)
    {
        s[0] = '0';
        s[1] = 0; //在有效位之後添加\0
        return;
    }
    i = 0;
    while(n) //判斷有沒有取完所有位
    {
        temp[i++] = n % 10; //先存入,後i++
        n /= 10;
    }
    i -= 1;
    j = 0;
    while(i >= 0)
        s[j++] = temp[i--] + '0'; //將倒序的數組正序存入s數組,s數組存有最終結果
    s[j] = 0;
}

    ②重新定義字符串的運算,以乘法爲例,算法如下:

void mul(char* m, char* n, char* res)
{
    int i,j,len1,len2;
    len1 = strlen(m);
    len2 = strlen(n);
    int *r = new int[len1 + len2 + 1]; //乘積的長度,例如兩位乘以兩位,最多爲5位,因此多加1
    for(i = 0; i <= len1 + len2; i++) r[i] = 0; //初始化乘積存儲數組
    for(i = 0; i < len1; i++)
        for(j = 0; j < len2; j++)
            r[i + j + 1] += (m[i] - '0')*(n[j] - '0'); //依次從最高位、次高位直至最低位進行運算,注意,這裏的最高位實際爲次高位,因爲
                                                       //真正的最高位只能通過進位獲得,而不是通過乘運算
    for(i = len1 + len2 - 1; i >=1; i--) //從最低位開始處理進位
    {
        if(r[i] > 9) 
        {
            int temp = r[i] / 10; //儲存進位數
            r[i] %= 10; //將進位後的數組存在這一位
            r[i-1] += temp; //進位運算,同時處理r[0]這一位(進位可能到達的最高位,沒有則爲0)
        }
    }
    for(i = 0; i<len1+len2;i++) cout << r[i] << " ";
    cout << endl;
    //經過這樣的運算,將會得到數據字符串,其中最高位在r[0]內,最低位在r[len1+len2-1]內
    for(i = 0; i < len1+len2; i++) //判斷是否乘積爲0,爲0則i會自加到len1+len2
        if(r[i] != 0) break;
    if(i == len1 + len2)
    {
        res[0] = '0';
        res[1] = 0;
        return;
    }
    //如果乘積不爲0,會進行下面的運算,順次將r[0]到r[len1+len2-1]存入res[0]到res[len1+len2-1]
    j = 0;
    while(i < len1 + len2)
    {
        res[j++] = r[i++] + '0';
    }
    res[j] = 0;
    delete[] r;
}


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