1. 程式人生 > >最少硬幣問題(多重揹包問題)

最少硬幣問題(多重揹包問題)

最少硬幣問題

問題描述:設有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

。問題無解時輸出-1

這個題目,用一個二維的動態轉移方程比一維動態轉移方程更容易理解。

下面的解法,是基於一維動態轉移方程。

/********************************************************************
 ** @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;
}