动态规划应用--找零钱
生活随笔
收集整理的這篇文章主要介紹了
动态规划应用--找零钱
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 1. 問題描述
- 2. 問題分析
- 2.1 回溯法求解
- 2.2 DP狀態轉移方程法
- 2.3 DP狀態轉移表法
1. 問題描述
找零問題,在貪心算法講過。但是貪心不一定能得出最優解。假設有幾種不同幣值的硬幣v1,v2,.……vn(單位是元)。如果要支付w元,求最少需要多少個硬幣。比如,有3種不同的硬幣,1元、3元、5元,我們要支付9元,最少需要3個硬幣(3個3元的硬幣)。
2. 問題分析
2.1 回溯法求解
/*** @description: 找零錢,需要張數最少,回溯法* @author: michael ming* @date: 2019/7/20 22:50* @modified by:*/ #include <iostream> #define N 3 int rmb[N] = {1,9,10};//鈔票面額 int amount[N]; int minAmount[N]; using namespace std; void exchange(const int &targetMoney, int curMoney, int &minPiece, int piece) {if(curMoney > targetMoney)//超過目標,返回return;if(curMoney == targetMoney)//達到目標金額{if(piece < minPiece){minPiece = piece;//更新最小張數for(int i = 0; i < N; ++i)minAmount[i] = amount[i];//獲取每張鈔票的張數}return;}for(int i = 0; i < N; ++i){//遞歸調用,拿取每張面額的鈔票amount[i]++;exchange(targetMoney,curMoney+rmb[i],minPiece,piece+1);amount[i]--;//恢復上次的狀態} } int main() {int minPiece = 65535, piece = 0,targetMoney = 18, curMoney = 0;exchange(targetMoney,curMoney,minPiece,piece);cout << "湊成" << targetMoney << "元,最少需要:" << minPiece << "張(枚)。" << endl;int i = 0;while(i < N){if(minAmount[i] != 0)cout << minAmount[i] << "個" << rmb[i] << " ";i++;}cout << endl;cout << "----------------------" << endl; }2.2 DP狀態轉移方程法
由于上面的鈔票面額可能不止3種,遞歸樹是多叉樹,所以狀態轉移表法畫起回溯的遞歸圖比較麻煩,我們采用狀態轉移方程法。
狀態轉移方程如下:
minPiece(targetMoney) = 1 + min{minPiece(targetMoney-rmb[0]), ... , minPiece(targetMoney-rmb[N-1])}targetMoney = 18;//目標金額
rmb[N] = {1,9,10};//鈔票面額
對于題目的情況,代入具體數值,狀態轉移方程如下
DP(遞歸+備忘錄)代碼如下:
/*** @description: 找零錢,需要張數最少* @author: michael ming* @date: 2019/7/20 18:35* @modified by: */ #include <iostream> #include <algorithm> #include <memory.h>#define N 3 const int targetMoney = 18;//目標金額 int rmb[N] = {1,9,10};//鈔票面額 int mem[targetMoney+1];//備忘錄,存放最小張數 using namespace std; int minP(int Money) {if(Money < 0)//超過目標,返回很大的張數,表示不可能湊成return 65535;if(Money == 0)//達到目標金額return 0;if(mem[Money] > 0)//計算過了,直接讀取備忘錄return mem[Money];int minAmount[N];memset(minAmount,65535,N*sizeof(int));for(int i = 0; i < N; ++i){//遞歸調用,拿取每張面額的鈔票minAmount[i] = minP(Money-rmb[i]);}sort(minAmount,minAmount+N);mem[Money] = minAmount[0]+1;//記錄最小的張數return mem[Money]; } int main() {cout << "湊成" << targetMoney << "元,最少需要:"<< minP(targetMoney) << "張(枚)。" << endl;//如何打印出選取鈔票的面額和張數??? }2.3 DP狀態轉移表法
/*** @description: 找零錢,需要張數最少,dp狀態表法* @author: michael ming* @date: 2019/7/21 20:01* @modified by: */ #include <iostream> #include <algorithm> #include <memory.h>#define N 3 const int targetMoney = 18;//目標金額 int rmb[N] = {1,9,10};//鈔票面額,從小到大 using namespace std; void exchange(int Money) {int maxPiece = targetMoney/rmb[0];//最大張數int i, j, k;int (*states)[targetMoney+1] = new int [maxPiece][targetMoney+1];//memset(states,65535,maxPiece*(targetMoney+1)*sizeof(int));//上面錯誤!!!memset一般只付0或極大值for(i = 0; i < maxPiece; ++i)for(j = 0; j <= targetMoney; ++j)states[i][j] = 65535;//初始化for(k = 0, j = 0; j <= targetMoney; ++j){if(k < N && j == rmb[k]){//初始化第一行數據states[0][j] = 1;//一張rmbk++;}}for(i = 1; i < maxPiece; ++i)//動態規劃{for(j = 0; j <= targetMoney; ++j)//上面一行的數據考下來states[i][j] = states[i-1][j];for(j = 0; j <= targetMoney; ++j){if(states[i-1][j] != 65535){for(k = 0; k < N; ++k){if(j+rmb[k] <= targetMoney && states[i-1][j+rmb[k]] > states[i-1][j]+1)states[i][j+rmb[k]] = states[i-1][j]+1;}}}}cout << "湊成" << targetMoney << "元,最少需要:"<< states[maxPiece-1][targetMoney] << "張(枚)。" << endl;//------------打印選擇的信息---------------------------for(i = maxPiece-1; i >= 1 && states[i][targetMoney] == states[i-1][targetMoney]; --i);//此時i等于最早出現的答案處的行for(j = targetMoney; j > 0; ){if(i != 0){for(k = 0; k < N; ++k){if(states[i-1][j-rmb[k]] == states[i][j]-1){cout << "1張" << rmb[k] << " ";j = j-rmb[k];i--;break;}}}else{cout << "1張" << j << " ";break;}}delete [] states;//釋放資源 } int main() {exchange(targetMoney);return 0; }
總結
以上是生活随笔為你收集整理的动态规划应用--找零钱的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构--单链表single link
- 下一篇: 数据结构--链表--LRU缓存