日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态规划应用--找零钱

發布時間:2024/7/5 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划应用--找零钱 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 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};//鈔票面額
對于題目的情況,代入具體數值,狀態轉移方程如下

minPiece(18) = 1 + min{minPiece(18-1), minPiece(18-9) , minPiece(18-10)}= 1 + min{minPiece(17),minPiece(9),minPiece(8)}

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; }


總結

以上是生活随笔為你收集整理的动态规划应用--找零钱的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。