动态规划专题----入门(一)---个人心得

什么是动态规划

dynamic programming is a method for solving a complex problem by breaking it down into a collection of simpler subproblems

                                                                                                                                                           ------- 引用维基百科

翻译过来就是动态规划就是通过拆分问题,来解决复杂的问题的一个方式

所以我们也知道,动态规划的核心就是如何取拆分这个问题

那如何拆分问题,这个也就成为我们要去研究的重点

借鉴于大部分权威人士比较认同的观点是:

拆分问题,靠的是状态的定义状态转移方程的定义

这里有一遍知乎是对上面的定义的见解(ps:个人还没看懂,之后在慢慢深究)

https://www.zhihu.com/question/23995189/answer/35324479

什么是状态的定义

其实就是解动态规划需要开一个数组数组,数组中的每一个元素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.

Example

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

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

Notice

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

问题简单的解释下就是给你不同币值的硬币,凑足一个给定的金额,不能多不能少,得出最少的硬币个数,如果没有,返回-1的意思

那我们重新返回之前说的状态的定义的两个步骤

最后一步:

我们就从例子里面说的,有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];
        }
    }

最后一点就是

什么时候能用上动态规划:

这个有个截图已经说明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值