問題:設購票點沒有任何的零錢,票價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數,公式爲:
在運算時,要特別注意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; }