動態規劃-硬幣問題分析

什麼是動態規劃

上次對動態規劃已經有了個大概的分析。引用維基百科的話就是:
dynamic programming is a method for solving a complex problem by breaking it down into a collection of simpler subproblems算法

翻譯過來就是動態規劃就是經過拆分問題,來解決複雜的問題的一個方式。因此咱們也知道,動態規劃的核心就是如何取拆分這個問題那如何拆分問題,這個也就成爲咱們要去研究的重點。借鑑於大部分權威人士比較認同的觀點是:拆分問題,靠的是狀態的定義和狀態轉移方程的定義。
這裏有一遍知乎是對上面的定義的看法:www.zhihu.com/question/23…後端

什麼是狀態的定義

咱們對狀態的定義是這樣的:解答動態規劃問題,須要開一個數組,數組中的每個元素f[i]或f[i][j]表明了什麼。而狀態的定義須要兩個步驟:
1.最後一步;
2.子問題。數組

題目描述

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.bash

Example Given coins = [1, 2, 5], amount = 11 return 3 (11 = 5 + 5 + 1)函數

Given coins = [2], amount = 3 return -1.ui

Notice You may assume that you have an infinite number of each kind of coin.spa

問題簡單解釋:現有不一樣幣值的硬幣,湊足一個給定的金額,不能多不能少,得出最少的硬幣個數;若是無解,返回-1。翻譯

解題思路

那咱們從新返回以前說的狀態的定義的兩個步驟。
最後一步:code

咱們就從例子裏面說的,有1,2,5三個幣值的硬幣,湊足11塊。咱們假設最優策略是k個硬幣湊足了11塊,最後的一塊硬幣幣值是a(K)元,則前面的硬幣拼出的幣值爲11-a(K)元,這就是先着手最後一步的狀態定義。
而後從子問題講:
因爲咱們已經從最後一步定義,將原問題轉化成另一個問題是用最少的硬幣湊足11-a(K)元,此時將原規模變小,而此時的問題就是原問題的子問題。能夠將湊足多少元(即規模)定義爲X,則能夠用狀態f(X) = 用最少的硬幣湊足X元。咱們假設最後一枚硬幣是1,則f(11) = f(11 -1)+1枚;最後一枚是2,則f(11) = f(11-2)+1枚;同理,最後一枚爲5,則f(11)=f(11-5)+1枚,即其實最少的硬幣爲f(11)=min{f(11-1)+1,f(11-2)+1,f(11-5)+1}。 咱們能夠用程序進行表示:遞歸

// 遞歸解法 
// 沒有考慮邊界問題
僞代碼
int f(x){
if(x==0)return 0;
int res = Integer.MAX_VALUE;
if(x>=1){
res = Math.min(f(x-1)+1,res);
}
if(x>=2){
res = Math.min(f(x-2)+1,res);
}
if(x>=5){
res = Math.min(f(x-5)+1,res);
}
return res;
}
複製代碼

但遞歸法解法作了不少重複計算,好比f(9)->f(11-1)->f(10-9)或者f(11-2)計算可得。這時候咱們能夠從動態規劃第二部分---狀態的轉移。

其實很簡單將f(x) 變成一個數組f[x],將計算結果保存起來。此時函數f[x]=min{f[x-1]+1,f[x-2]+1,f[x-5]+1}。
因爲是個數組,因此咱們應該考慮下邊界和初始值的問題。假設拼不出來的值如f[-1]或者f[-3]這種,咱們定義爲正無窮Max_Value,初始值f[0]=0表明要拼出0元即爲0個硬幣。此時咱們應當從小到大順序算出每一個元素的最小硬幣值,即:
f[1]=min{f[1-1]+1,f[1-2]+1,f[1-5]+1}=f[0]+1 = 1

f[2] = min{f[2-1]+1,f[2-2]+1,f[2-5]+1} = f[0]+1 = 1,

咱們不難發現,當f[2]以1塊爲最後的硬幣時,f[1]其實已經保存該值的最小硬幣數,不會重複計算,且算法時間度是X*3

能夠看下程序:

public int coinChange(int[] a, int M) {
        // write your code here
        int length = a.length;
        int []f = new int[M+1];

        // 初始化
        f[0]=0;
        for(int i=1;i<=M;i++){
            // 先將f[i]初始無窮大,進行對比
            f[i]= Integer.MAX_VALUE;
            for (int j=0;j<length;j++){
                // 湊足的金額必定得大於硬幣的值且減了硬幣不該該等於無窮大
                if(i>=a[j]&&f[i-a[j]]!=Integer.MAX_VALUE&&f[i-a[j]]+1<f[i]){
                    f[i]= f[i-a[j]]+1;
                }
            }
        }
        if(f[M]==Integer.MAX_VALUE){
            return -1;
        }else {
            return f[M];
        }
    }
複製代碼

做者簡介

考拉後端開發小哥Rowland,熱愛運動還有點萌!

相關文章
相關標籤/搜索