動態規劃-DAG-硬幣問題
阿新 • • 發佈:2019-02-04
題目:有n種硬幣,面值分別為V1,V2,…Vn,每種都有無限多。給定非負整數S,可以選用多少個硬幣,使得面值之和恰好為S?輸出硬幣數目的最小值和最大值!
若用記憶化搜尋,需構建2個dp函式,如下為只寫了求最大數量的程式碼
#include<iostream> #include<cstring> #include<algorithm> using namespace std; int d[100]; //表示d[S],湊齊S需要最多的硬幣數 int n=3; //硬幣有3種種類 int v[4] = { 1,3,5 }; int dp(int s) { int i; int& ans = d[s]; //重新引用ans,是當d[i]換成d[i][j][k]時,該技巧的優勢就會出來 if (ans != -1) { return ans; } //ans = -(1 << 30); //位移運算,在數字沒有溢位的前提下,對於正數和負數,左移一位都相當於乘以2的1次方,左移n位就相當於乘以2的n次方。 ans = -10; for (i = 0; i < n; i++) { if (s >= v[i]) { ans = max(ans, dp(s - v[i]) + 1); } } return ans; } int main() { int i, sum, ans = -1; memset(d, -1, sizeof(d)); d[0] = 0; //若結束,為0,如果不設定會出錯 cout << "請輸入想要計算的金額 "; cin >> sum; //想要湊齊的金額 for (i = 0; i < n; i++) { ans = max(ans, dp(sum - v[i]) + 1); } cout << "需要的最多的硬幣數為 " << ans; return 0; }
如果想計算最大和最小,用遞推比較方便,先計算金額1需要多少個,再計算2需要多少,一直到s,且不會重複計算
#define INF 0x3f3f3f3f #include<iostream> #include<cstring> #include<algorithm> using namespace std; int minv[100],maxv[100]; //表示d[S],湊齊S需要最多的硬幣數 int n=3; //硬幣有3種種類 int v[4] = { 1,3,5 }; void print_ans(int *d,int s) { int i; for (i = 0; i < n; i++) { if (s >= v[i] && d[s] == d[s-v[i]] + 1) { cout << v[i] << " "; print_ans(d, s - v[i]); break; } } } int main() { int i,j,sum; memset(minv, INF, sizeof(minv)); //最開始要設定成很大的數 memset(maxv, -INF, sizeof(maxv)); minv[0] = maxv[0] = 0; //若結束,為0,如果不設定會出錯 cout << "請輸入想要計算的金額 "; cin >> sum; //想要湊齊的金額 for (i = 1; i <= sum; i++) { for (j = 0; j < n; j++) { if (i >= v[j]) { minv[i] = min(minv[i], minv[i - v[j]] + 1); maxv[i] = max(maxv[i], maxv[i - v[j]] + 1); } } } cout << "需要的最多的硬幣數為 " << maxv[sum]<<endl; cout << "硬幣金額為 "; print_ans(maxv, sum); cout << endl; cout << "需要的最少的硬幣數為 " << minv[sum] << endl; cout << "硬幣金額為 "; print_ans(minv, sum); return 0; }
還有一種用空間代替時間的例子,用min_coin和max_coin消除了原來print_ans的迴圈
#define INF 0x3f3f3f3f #include<iostream> #include<cstring> #include<algorithm> using namespace std; int minv[100],maxv[100]; //表示d[S],湊齊S需要最多的硬幣數 int n=3; //硬幣有3種種類 int v[4] = { 1,3,5 }; void print_ans(int *d,int s) { while (s) { cout << d[s]<<" "; s = s - d[s]; } } int main() { int i,j,sum; int min_coin[100], max_coin[100]; //用空間代替時間 memset(minv, INF, sizeof(minv)); //最開始要設定成很大的數 memset(maxv, -INF, sizeof(maxv)); minv[0] = maxv[0] = 0; //若結束,為0,如果不設定會出錯 cout << "請輸入想要計算的金額 "; cin >> sum; //想要湊齊的金額 for (i = 1; i <= sum; i++) { for (j = 0; j < n; j++) { if (i >= v[j]) { if (minv[i] > minv[i - v[j]] + 1) { minv[i] = min(minv[i], minv[i - v[j]] + 1); min_coin[i] = v[j]; } if (maxv[i] < maxv[i - v[j]] + 1) { maxv[i] = max(maxv[i], maxv[i - v[j]] + 1); max_coin[i] = v[j]; } } } } cout << "需要的最多的硬幣數為 " << maxv[sum]<<endl; cout << "硬幣金額為 "; print_ans(max_coin, sum); cout << endl; cout << "需要的最少的硬幣數為 " << minv[sum] << endl; cout << "硬幣金額為 "; print_ans(min_coin, sum); return 0; }
結果