当前位置: 首页 > article >正文

动态规划算法

一、前言

动态规划是一种常用的算法,在算法领域十分重要,但对于新手来说,理解起来有一定的挑战性,这篇博客将明确步骤来一步一步讲解动态规划到底该如何理解与运用。

二、解析动态规划算法

1.特点

①把原来的问题分解成了【要点相同】的子问题(这个要点可以和后面讲的状态联系起来理解)

②所有问题都只需要解决一次(解决一次就是只需要由后面所说的状态转移方程解决即可)

前这两个特点可以看出,其实动态规划就是以对解答问题的每个步骤具化成状态,用通过状态与状态之间的递推关系写出的状态转移方程来解决实际问题的算法

③储存子问题的解(状态转移方程中往大问题转移一定需要子问题的解,所有需要将子问题的解储存起来)

2.要素

①状态的定义

②状态间的转移方程定义

③状态的初始化

(初始状态确定已知)

④返回结果

(返回的结果恰好完全符合题目要求)

3.用动态规划解题步骤

①根据题目所给条件,以及所问问题,初步判断是否具有动态规划的特点(可分治,可转移,可储存)

②对状态做出定义后判断当前状态可以由哪几种子状态通过哪几种方式(紧扣题目条件)得到,并以此为依据写出状态转移方程,并找全四个要素

③写代码

4.例题

题目

跳台阶扩展问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法

分析

所以要想跳到第n个台阶,我们可以从第1个台阶跳上来,也可以从第2个台阶跳上来……,很明显有小问题推大问题的情节
递推公式是
f(n)=f(n-1)+f(n-2)+……+f(2)+f(1);
同样如果我们想跳到第n-1个台阶,也可以列出下面这个公式
f(n-1)=f(n-2)+……+f(2)+f(1);
通过这两个公式我们可以得出
f(n)=2*f(n-1)
实际上他是个等比数列,base case当n等于1的时候结果为1,来看下代码

代码

class Solution {
public:
    int jumpFloorII(int number) 
    {
        if(number==0||number==1)
        {
            return number;
        }
        else
        {
            int ret=1;
            while(number>1)
            {
                ret=ret*2;
                number--;
            }
            return ret;
        }
    }
};

题目

最大连续子数组和
输入一个整形数组(可能有正数和负数),求数组中连续子数组(最少有一个元素)的最大和。要求时间复杂度为O(n)

分析

先假设以索引i-1结束时为状态,那么这个状态下时最大和怎么推导索引i结束时最大和呢,逻辑思考后可以发现,真实的最大连续子数组和肯定以某个索引结束的,所以找到dp中最大值即可。填充dp时,如果dp[i-1] < 0,那么dp[i]直接等于第i个数nums[i],否则就是dp[i] = dp[i-1] + nums[i]。

代码

 int main () {
    int n = 0, temp = 0;
    cin >> n;
    vector<int>  nums, dp(n, 0);
    while (cin >> temp) nums.push_back(temp);
    dp[0] = nums[0];
    for (int i = 1; i < n; i++) {
        if (dp[i-1] < 0) dp[i] = nums[i];
        else dp[i] = dp[i-1] + nums[i];
    }
    cout << *max_element(dp.begin(), dp.end()) << endl;
    return 0;
}

题目

单词拆分
给定一个字符串和一个字符串数组,在字符串的任意位置拆分任意次后得到的字符串集合是否是给定字符串数组的子集。

分析

出发点:给定字符串s的每个字符i
最优解:对于每个i之前的字符序列,分为j(0<=j<=i)前面的序列和j后面的序列,求解i转化为求解j和i-j
状态d(i):i之前的字符序列分段后的字符串能不能在字典中找到
递推公式:d(i) = d(i)||(d(j)&sub(i-j)), 其中j=0~i

代码

#include
using namespace std;
class Solution {
public:
    bool wordBreak(string s, unordered_set &dict) {
        // 动态规划:1\. 每一个状态和前一个状态有关;2\. 最小子状态是可以求得的
        // 转移方程:f(i)表示s[0,i]可以分词,那么f(i) = f(j) && f(j+1, i); (0<=j<i)
        int len = s.length();
        vector dp(len+1, false);
        dp[0] = true;
        for (int i = 1; i <= len; ++i) {        // 注意终止条件,此处从1开始,终止条件应该为len
            for (int j = i-1; j >= 0; --j) {
                if (dp[j] && dict.find(s.substr(j, i-j)) != dict.end()) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[len];
    }
};

~感谢观看❥(^_-)


http://www.kler.cn/a/1729.html

相关文章:

  • csrf跨站请求伪造(portswigger)无防御措施
  • 开源简史与概览
  • 【Vim Masterclass 笔记03】S03L10 + S03L11:Vim 中的文本删除操作以及 Vim 思维习惯的培养(含 DIY 拓展知识点)
  • CSV vs 数据库:爬虫数据存储的最佳选择是什么
  • Ubuntu 下使用命令行将 U 盘格式化为 ext4、FAT32 和 exFAT 的详细教程
  • DAY178内网渗透之内网对抗:横向移动篇入口差异切换上线IPC管道ATSC任务Impacket套件UI插件
  • 断言assert
  • 【多线程】多线程案例
  • GitHub 上有些什么好玩的项目?
  • 不是,到底有多少种图片懒加载方式?
  • 【正点原子FPGA连载】 第三十三章基于lwip的tftp server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
  • 修剪灌木[蓝桥杯2022初赛]
  • Java基础:笔试题
  • sharding-jdbc四种分片策略
  • 复制带随机指针的复杂链表
  • 【二】一起算法---队列:STL queue、手写循环队列、双端队列和单调队列、优先队列
  • 【微信小程序】-- 分包 - 独立分包 分包预下载(四十五)
  • MySQL高级功能:存储过程、触发器、事务、备份和恢复
  • windows安装包管理工具Chocolatey
  • Java 到底是值传递还是引用传递?
  • java基础面试题(一)
  • Windows安装部署nginx
  • 【JavaEE】进程和线程
  • 自动驾驶TPM技术杂谈 ———— 超声波雷达系统测距
  • C++修炼之筑基期第一层——认识类与对象
  • 函数指针->回调函数