初学动态规划法

前言

最近在做力扣里面的算法题,发现很大一部分都是跟动态规划法有关,所以决定恶补下。

什么是动态规划法?

按百度百科的话说->动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法.
它的基本思想是把问题拆分成一个个的子问题,定义问题的转换关系,由初始状态经过递推到达结束状态,从而解决问题。

基本满足的性质

  • 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
  • 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
  • 有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

常见的动态规划算法题主要有数字三角形问题,背包问题等。

例题一:数字三角形问题

题目:给出一个数字三角形,从顶点出发每次向下或者右下走一步直至最底层,将途径的数字相加问可以得到得最大值。
3
3 6
3 8 4
1 2 5 7

求解思路:
根据动态规划得思想,可以将该题拆分成个个问题,每个子问题得解都由上一个问题递推而来,因此,假设有i层,从顶点到达则第i层第j个数字途径数字相加得最大值由第i-1层第j个数字得最大值或者第i-1层第j-1个数字得来。

public static void demo(int[][] a){
        int N = a.length;
        int[][] f=new int[N][N];
        f[0][0]=a[0][0];
        for(int i=1;i<N;i++){
            for(int j=0;j<a[i].length;j++){
            // 也可以通过补零得方式,使得左上或者正上不为空
                if(j==0){
                    f[i][j]=f[i-1][j]+a[i][j];
                }else if(j==a[i].length-1){
                    f[i][j]=f[i-1][j-1]+a[i][j];
                }else {
                    f[i][j]=Math.max(f[i-1][j],f[i-1][j-1])+a[i][j];
                }
            }
        }
    }

本人菜鸡代码仅仅提供一个思路,欢迎大家评论优化方法。

例题二:

给你一个序列求最长上升子序列。
例如:1、23、13、4、7、5、15
思路还是差不多直接附上代码吧

public static void demo2(int[] a){
        int[] f=new int[a.length];
        f[0]=1;
        for(int i=1;i<a.length;i++){
            for(int j=0;j<i;j++){
                if(a[i]>a[j]) {
                    f[i] = f[j] + 1;
                }
            }
        }
    }

最后直接输出数组中得最大值就好了

例题三:背包问题

01背包

01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn。01背包是背包问题中最简单的问题。01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。

题目:有N件物品和一个容量为V的背包。第i件物品的容量是w[i],价值是v[i],求将哪些物品装入背包可使价值总和最大。
解:假设f[i][j]表示第i件物品体积为j得价值,则
解题得核心为

f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i])

即第i件物品要么取要么不取。

用二维数组求解代码为:


for(i = 1; i<=n; i++)
{
    for(j = 0; j<=W; j++)
    {
        if(j<w[i])
            f[i][j] = f[i-1][j];
        else
            f[i][j] = max(f[i-1][j], f[i-1][j-w[i]] + v[i]);
    }

可以在此得基础上对空间复杂度进行优化,将二维数组降维,

在这里插入图片描述
第i层只于第i-1层有关,可以通过滚动数组方式,将数组得值一层一层得覆盖,最后得到的数组就是第i层的最大价值的数组
核心代码:

for (int i = 1; i <= N; i++)
    for (int j = V; j >= 0; j--)
        f[j] = max(f[j], f[j - w[i]] + v[i]);

进一步优化:因为i只关心第i-1层的数组,又i的容量为w[i],所以无论如何第i层只会更新数组中j>=w[i]的值。其余的将还是i-1层的值。

for (int i = 1; i <= N; i++)
    for (int j = V; j >= w[i]; j--)
        f[j] = max(f[j], f[j - w[i]] + v[i]);

最后常数级优化:

for (int i = 1; i <= n; i++) {
    int bound = max(V - sum{w[i]...w[n]}, w[i]);
    for (int j = V; j >= bound, j--)
        f[j] = max(f[j], f[j - w[i]] + v[i]);
}

em…我也不太理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值