UVA 674 硬幣問題(完全揹包、列舉)
阿新 • • 發佈:2018-11-09
題意:給定金額n,有50,25,10,5,1這五種面值的錢,問共有多少種不同的找法(假設最少存在一種找法)。
這題我想了很久,都沒有找到合適的狀態轉移方程,最後看了別人寫的,又體會了半天,才算明白。
分析:
這題,寫不對原因在於,很容易就重複計算了。例如 11 中的 (11111,5,111111)與(11111,111111,5)視為一種。
那麼怎麼避免重複計算呢。
首先從小的到大列舉面額(這個必須在外面迴圈)
比如 列舉 dp[0] = 1,dp[1] = 1,dp[2] = 1,d[3] = 1,dp[4] = 1
當列舉到dp[5]的 時候 第一遍 的結果是 dp[5] += dp[5-1] 此時 dp[5] = dp[4] = 1(實際上dp[5] = 2),下一個迴圈列舉到
dp[5]的時候,dp[5] += dp[5-5] = 2。這樣單獨看某個值,最後都會遍歷到每一個面值,也就是最終會滿足dp方程
這樣 第一遍 dp[6] += dp[6-1] =>dp[6] = dp[5] = 1, 第二遍 dp[6] += dp[6-5] =>dp[6] = 1 + dp[1] 不會重複。
code:
#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<vector> #include<map> #include<algorithm> #include<string> using namespace std; const int N = 10000; int cost[10] = {1,5,10,25,50}; int dp[N]; int main() { // dp[i] 表示 金錢 i 可以有多少種表示方法 int n; memset(dp,0,sizeof(dp)); dp[0] = 1; for(int i = 0; i < 5; ++i){ // 兩層 迴圈不可互換 否則 重複 for(int j = cost[i]; j < N; ++j){ dp[j] += dp[j-cost[i]]; } } while(scanf("%d",&n) != EOF){ printf("%d\n",dp[n]); } return 0; }