最少硬幣問題(多重揹包問題)
阿新 • • 發佈:2019-02-02
最少硬幣問題
問題描述:設有n種不同面值的硬幣,各硬幣的面值存在於陣列T[1:n]中。現要用這些面值的硬幣來找錢。可以使用的各種面值的硬幣個數存於陣列Coins[1:n]中。對任意錢數0£m£20001,設計一個最少硬幣找錢m的方法。
演算法設計:對於給定的1£n£10 ,硬幣面值陣列T和可以使用的各種面值的硬幣陣列Coins,以及錢數m, 0£m£20001,計算找錢m的最少硬幣數。
資料輸入:由檔案input.Txt提供輸入資料,檔案的第1行中只有一個整數給出n的值,第2行起每行兩個數,分別是T[j]和Coins[j]。最後一行是要找的錢數m。
結果輸出:將計算出的最少硬幣數輸出到檔案output.txt
這個題目,用一個二維的動態轉移方程比一維動態轉移方程更容易理解。
下面的解法,是基於一維動態轉移方程。
/******************************************************************** ** @file test.cpp ** @author liuke ** @date Fri Apr 22 23:50:50 2011 ** @brief **************************動態規劃實現******************************** 長度為m的陣列f[1...m]中存放一系列子結果,即f[i]為要湊的錢數為i時 所需的最少硬幣數,則c[m]為所求; 當要找的錢數i(1<i<m)與當前所試探的硬幣面值k相等時,結果為1,即c[i]=1 當i大於當前所試探硬幣面值k時,若f[i]為0,即還未賦過值,且c[i-k]不為0, 即從i元錢中刨去k元后剩下的錢數可以找開, 則c[i]=c[i-k]+1 若f[i]不為0,即已賦過值,則f[i]為f[i-k]+1和f[i]中較小的 **
//硬幣問題就是一個多重揹包問題 //動態遷移方程為 dp[k] = min{dp[k-t[i]]+1,dp[k]} //就是,將第i個硬幣拿出去得到的一個最少的找硬幣數+1,和原硬幣數相比最小的那個就是結果 //另外一種思路,可以將所有的硬幣價值都放在一個數組,就變成了0-1揹包問題,所需考慮的就是放不放的問題 #include<iostream> using namespace std; int min(int a,int b); int main() { int n; //n種不同面值的硬幣 int m; int i , j ,k; cout<<"請輸入有幾種不同的面值:"; cin>>n; int *t =new int[n+1]; //硬幣的面值存放在t陣列中 -- 價值 int *coin = new int [n+1]; //可以使用的硬幣個數存放在coin中--個數 cout<<"請輸入"<<n<<"組硬幣的面值和對應的個數(中間用空格隔開):"<<endl; for(i = 1 ;i<n+1;i++) cin>>t[i]>>coin[i]; cout<<"請輸入要找的錢數m:"; cin>>m; int dp[20002]={0} ; //dp[i] 用來記錄錢數為i時的最少的硬幣數 for(i=1;i<=m;i++) dp[i] = 99999; //dp[0] = 0; for(i = 1 ;i <= n ; i++) //硬幣面值的種數 for(j = 1 ; j <= coin[i] ; j++) //硬幣的面值的個數 for( k = m ; k >= t[i] ; k-- ) { dp[k] = min(dp[k-t[i]] +1,dp[k]); //cout<<k<<": "<<dp[k]<<endl; 用於測試使用 } cout<<"最少需要用到的硬幣個數是:"; if(dp[m] == 99999)cout<<-1<<endl; else cout<<dp[m]<<"個"<<endl; } int min(int a,int b) { return a<b?a:b; }