NYOJ 995 硬幣問題(經典動態規劃)
硬幣找零
時間限制:1000 ms | 記憶體限制:65535 KB
難度:3
輸入
輸入資料: 第 1 行,為 N 和 T,其中 1≤N≤50 為硬幣系統中不同硬幣數;1≤T≤100000 為需要用硬幣找零的總數。 第 2 行為 N 個數值不大於 65535 的正整數,它們是硬幣系統中各硬幣的面值。 當n,t同時為0時結束。
輸出
輸出資料: 如 T 能被硬幣系統中的硬幣找零,請輸出最少的找零硬幣數。 如 T 不能被硬幣系統中的硬幣找零,請輸出剩下錢數最少的找零方案中的最少硬幣數。
樣例輸入
4 12
10 7 5 1
樣例輸出
2
描述
在現實生活中,我們經常遇到硬幣找零的問題,例如,在發工資時,財務人員就需要計算最少的找零硬幣數,以便他們能從銀行拿回最少的硬幣數,並保證能用這些硬幣發工資。
我們應該注意到,人民幣的硬幣系統是 100,50,20,10,5,2,1,0.5,0.2,0.1,0.05,
0.02,0.01 元,採用這些硬幣我們可以對任何一個工資數用貪心演算法求出其最少硬幣數。
但不幸的是: 我們可能沒有這樣一種好的硬幣系統, 因此用貪心演算法不能求出最少的硬幣數,甚至有些金錢總數還不能用這些硬幣找零。例如,如果硬幣系統是 40,30,25 元,那麼 37元就不能用這些硬幣找零;95 元的最少找零硬幣數是 3。又如,硬幣系統是 10,7,5,1元,那麼 12 元用貪心法得到的硬幣數為 3,而最少硬幣數是 2。
你的任務就是:對於任意的硬幣系統和一個金錢數,請你程式設計求出最少的找零硬幣數;
如果不能用這些硬幣找零,請給出一種找零方法,使剩下的錢最少。
分析:此題麻煩點在於,不一定能夠剛好找零,需要儘可能的找零(在不多給錢的情況下,被給錢的人拿到儘可能多的錢)
剛開始 想的 是 定義狀態 表示 金額為 時候 需要的最小硬幣數,後來發現一個問題,如果初始化 全為0 則 求出的最小值全為0,如果把 其它位 無窮大,這樣得到的結果為無窮大(此時只有恰好找零能夠得到結果)。 後來想到倒推的動態規劃方程。
其中
注意 倒推到 0 位置 而不是 1, 0表示恰好找零。然後輸出最小的 滿足 的 。
code:
#include<iostream> #include<stdio.h> #include<vector> #include<map> #include<cstring> #include<cstdlib> #include<algorithm> #include<set> #include<cmath> using namespace std; const int N = 100000 + 5; const int INF = 1 << 30; int dp[N]; int t,n; int cost[55]; int main() { while(scanf("%d%d",&n,&t) == 2){ if(n + t == 0) break; //memset(dp,INF,sizeof(dp)); fill(dp,dp+N,INF); for(int i = 0; i < n; ++i){ scanf("%d",&cost[i]); } dp[t] = 0; for(int i = t; i >= 0; --i){ for(int j = 0; j < n; ++j){ if(i+cost[j] <= t){ dp[i] = min(dp[i],dp[i+cost[j]] + 1); } } } for(int i = 0; i <= t; ++i){ if(dp[i] < INF){ //printf("i = %d, dp[i] = %d\n",i,dp[i]); printf("%d\n",dp[i]); break; } } } return 0; }