动态规划——从入门到放弃(未完待续)

基本思想

  1. 最优子结构
    子问题最优时母问题通过优化选择为最优解
  2. 子问题重叠
    母问题与子问题本质上是同一个问题
    (不同在被子问题传递的参数)
  3. 边界
    (相当于递归的终止条件)
  4. 子问题独立
    母问题对子问题选择时,当前被选择的子问题两两互不影响
  5. 做备忘录
    记录求解过的子问题的答案(记忆化搜索)

如何用动态规划解决问题

  1. 构造问题所对应的过程
  2. 思考过程最后一步有哪些选择情况
  3. 找到最后一步的子问题,确保符合子问题重叠,把子问题中不同的地方设置为参数
  4. 使子问题符合“最优子结构”
  5. 找到边界,考虑边界的各种处理方式
  6. 确保子问题独立
  7. 考虑如何做备忘录
  8. 写转移方程式

参考资料:
动态规划三部曲之一个故事教你透彻理解动态规划(一)

从暴力到动态规划

引例1:已知nums=[1,5,2,4,3], 求最长递增子序列长度

  • 暴力枚举/暴力搜索
    时间复杂度:O(n*2n)

    以"1"为起点,下一个可选"5、2、4、3",以此类推……

尝试写出递归的代码:

#include <iostream>
#define N 5
using namespace std;
int nums[N]={1,5,2,4,3};
int maxLen;
int L(int i);
int main()
{
    for(int i=0;i<N;i++){
        maxLen=max(maxLen,L(i));
    }
    cout<<maxLen;
    return 0;
}
int L(int i){
    if(i==N-1)
        return 1;
    int maxL=1;
    for(int j=i+1;j<N;j++){
        if(nums[j]>nums[i]){
            maxL=max(maxL,L(j)+1);
        }
    }
    return maxL;
}
  • 优化——记忆化搜索/递归树的剪枝
    在这里插入图片描述

观察一下递归树发现,其中有很多重复计算(如:在计算1、2、4时已经计算过4的值,而在1、4支上又算了一次)
解决办法:用一个表保存一下计算的值,如果曾计算过就直接取值,否则再进行计算,从而避免许多不必要的重复计算,这种办法也叫做“记忆化搜索”或者叫“剪枝”,就是常说的“用空间换取时间”。

进行“剪枝”优化的代码:

#include <iostream>
#include <bits/stdc++.h>
#define N 5
using namespace std;
int nums[N]={1,5,2,4,3};
int Len[N];
int maxLen;
int L(int i);
int main()
{
    memset(Len,-1,sizeof(Len));
    for(int i=0;i<N;i++){
        maxLen=max(maxLen,L(i));
    }
    cout<<maxLen;
    return 0;
}
int L(int i){
    if(Len[i]!=-1){
        return Len[i];
    }
    if(i==N-1){
        Len[i]=1;
        return 1;
    }
    int maxL=1;
    for(int j=i+1;j<N;j++){
        if(nums[j]>nums[i]){
            maxL=max(maxL,L(j)+1);
        }
    }
    Len[i]=maxL;
    return maxL;
}
  • 迭代/非递归的实现
    在这里插入图片描述
    由此可见,知道L(4)可以计算L(3),知道L(4)、L(3)可以计算L(2),以此类推……我们便找到了递推的方向(与递归相反)

动态规划代码实现:

#include <iostream>
#include <bits/stdc++.h>
#define N 5
using namespace std;
int nums[N]={1,5,2,4,3};
int Len[N];
int maxLen;
int main()
{
    Len[N-1]=1;
    for(int i=N-2;i>=0;i--){
        Len[i]=1;
        for(int j=i+1;j<N;j++){
            if(nums[i]<nums[j])
                Len[i]=max(Len[i],Len[j]+1);
        }
    }
    for(int i=0;i<N;i++){
        maxLen=max(maxLen,Len[i]);
    }
    cout<<maxLen;
    return 0;
}

引例2:
k好数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值