《程序員代碼面試指南--IT名企算法與數據結構題目最優解》 左程雲 著ios
換錢的方法數程序員
【題目】
給定數組arr, arr中全部的值都爲正數且不重複。
每一個值表明一種面值的貨幣,每種面值的貨幣能夠使用任意張,
再給定一個整數aim,表明要找的錢數,求換錢有多少種方法。面試
【舉例】
arr=[5,10,25,1],aim=0
組成0元的方法只有1種,就是全部面值的貨幣都不用。因此返回1.算法
arr=[j5,10,25,1],aim=15
組成15元的方法有6種,
5Y*3
10Y*1+5Y*1
10Y*1+1Y*5
1Y*10+5Y*1
5Y*2+1Y*5
1Y*15數組
任何方法都沒法組成2元,因此aim=2會返回0.數據結構
#include <algorithm> #include <iostream> #include <stack> #include <vector> #include <exception> using namespace std; void PrintMap(int ** map, int rows, int cols) { cout << "PrintMap--------------start" << endl; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { cout << map[i][j] << ","; } cout << endl; } cout << "PrintMap--------------End" << endl; cout << endl; }
///////////////////////////////////////////////////////解法1:暴力遞歸 int process1(int *arr, int length, int index, int aim) { int res = 0; if (index == length) { res = aim == 0 ? 1 : 0; } else { for (int i = 0; arr[index] * i <= aim; i++) { res += process1(arr, length, index + 1, aim - arr[index] * i); } } return res; } int coins1(int *arr, int length, int aim) { if (arr == nullptr || length <= 0 || aim <= 0) { return 0; } return process1(arr, length, 0, aim); }
////////////////////////////////////////////////解法2:暴力遞歸優化版。記憶搜索 ////p(index, aim)這樣的一個遞歸過程.用map[index][aim]保存已經計算過的遞歸過程的結果 int process2(int *arr, int length, int index, int aim, int **map, int rows, int cols) { int res = 0; if (index == length) { res = aim == 0 ? 1 : 0; } else { int mapValue = 0; for (int i = 0; arr[index] * i <= aim; i++) { mapValue = map[index + 1][aim - arr[index] * i]; if (mapValue != 0) { res += mapValue == -1 ? 0 : mapValue; } else { res += process2(arr, length, index + 1, aim - arr[index] * i, map, rows, cols); } } } map[index][aim] = res == 0 ? -1 : res; return res; } int coins2(int *arr, int length, int aim) { if (arr == nullptr || length <= 0 || aim <= 0) { return 0; } int**map = new int*[length+1]; for (int i = 0; i < length + 1; i++) { map[i] = new int[aim + 1]; } for (int i = 0; i < length + 1; i++) { for (int j = 0; j < aim + 1; j++) { map[i][j] = 0; } } int iResult = process2(arr, length, 0, aim, map, length+1, aim+1); PrintMap(map, length + 1, aim + 1); return iResult; }
////////////////////////////////////////////////////////////////解法3:動態規劃分析的方法、抽象出dp[i][j]是如何由上一行獲得的 //dp[i][j]的含義是,在使用arr[0...i]貨幣的狀況下,組成錢數j有多少種方法。 //.... //dp[N-1][aim]的值就是最終結果 int coins3(int *arr, int length, int aim) { if (arr == nullptr || length <= 0 || aim <= 0) { return 0; } int**dp = new int*[length]; for (int i = 0; i < length; i++) { dp[i] = new int[aim + 1]; } for (int i = 0; i < length; i++) { for (int j = 0; j < aim + 1; j++) { dp[i][j] = 0; } } ////第1列都爲0.dp[i][0] 都爲1,表示 組成錢數0的方法只有1種 for (int i = 0; i < length; i++) { dp[i][0] = 1; } //第1行中的某些項.dp[0][j],只使用arr[0]貨幣,能夠組成哪些數 for (int j = 1; j * arr[0] < aim+1; j++) { dp[0][j*arr[0]] = 1; } int num = 0; for (int i = 1; i < length; i++) { for (int j = 1; j < aim + 1; j++) { num = 0; for (int k = 0; j - arr[i] * k >= 0; k++) { num += dp[i - 1][j - arr[i] * k]; } dp[i][j] = num; } } int iResult = dp[length - 1][aim]; PrintMap(dp, length, aim + 1); return iResult; }
////////////////////////////////////////////////解法4:動態規劃,再次抽象 //dp[i][j] = dp[i-1][j] + dp[i][j-arr[i]] // int coins4(int *arr, int length, int aim) { if (arr == nullptr || length <= 0 || aim <= 0) { return 0; } int**dp = new int*[length]; for (int i = 0; i < length; i++) { dp[i] = new int[aim + 1]; } for (int i = 0; i < length; i++) { for (int j = 0; j < aim + 1; j++) { dp[i][j] = 0; } } ////第1列都爲0.dp[i][0] 都爲1,表示 組成錢數0的方法只有1種 for (int i = 0; i < length; i++) { dp[i][0] = 1; } //第1行中的某些項.dp[0][j],只使用arr[0]貨幣,能夠組成哪些數 for (int j = 1; j * arr[0] < aim + 1; j++) { dp[0][j*arr[0]] = 1; } int num = 0; for (int i = 1; i < length; i++) { for (int j = 1; j < aim + 1; j++) { dp[i][j] = dp[i - 1][j]; dp[i][j] += j - arr[i] >= 0 ? dp[i][j - arr[i]] : 0; } } int iResult = dp[length - 1][aim]; PrintMap(dp, length, aim + 1); return iResult; }
////////////////////////////////////////////////解法5:動態規劃,空間壓縮 //dp[i][j] = dp[i-1][j] + dp[i][j-arr[i]] int coins5(int *arr, int length, int aim) { if (arr == nullptr || length <= 0 || aim <= 0) { return 0; } int*dp = new int[aim+1]; for (int j = 0; j < aim + 1; j++) { dp[j] = 0; } //第1行中的某些項.dp[0][j],只使用arr[0]貨幣,能夠組成哪些數 for (int j = 1; j * arr[0] < aim + 1; j++) { dp[j*arr[0]] = 1; } for (int i = 1; i < length; i++) { for (int j = 1; j < aim + 1; j++) { dp[j] += j - arr[i] >= 0 ? dp[j - arr[i]] : 0; } } int iResult = dp[aim]; return iResult; }
////===============測試用例==================== void test1() { int arr[4] = { 5, 10, 25, 1 }; // int iResult1 = coins1(arr, sizeof(arr) / sizeof(int), 15); // cout << "iResult1:" << iResult1 << endl; //int iResult2 = coins2(arr, sizeof(arr) / sizeof(int), 15); //cout << "iResult2:" << iResult2 << endl; // int iResult3 = coins3(arr, sizeof(arr) / sizeof(int), 15); // cout << "iResult3:" << iResult3 << endl; // int iResult4 = coins4(arr, sizeof(arr) / sizeof(int), 15); // cout << "iResult4:" << iResult4 << endl; int iResult5 = coins5(arr, sizeof(arr) / sizeof(int), 15); cout << "iResult5:" << iResult5 << endl; } int main() { test1(); cout << endl; system("pause"); return 0; }