灵神笔记(1)----动态规划篇

文章目录

  • 介绍
  • 动态规划入门:从记忆化搜索到递推
    • 打家劫舍
      • 递归
      • 记忆化递归
      • 递推
      • 滚动变量
  • 背包
    • 0-1 背包
      • 递归写法
      • 记忆化递归
    • 目标和
      • 记忆化搜索
      • 递推
      • 两个数组
      • 一个数组
    • 完全背包
      • 记忆化递归搜索
    • 零钱兑换
      • 记忆化递归
      • 递推
    • 背包问题变形[至多|恰好|至少]
  • 最长公共子序列
    • 记忆化搜索
    • 递推
    • 两个一维数组
    • 一维数组
  • 编辑距离
    • 记忆化搜索
    • 递推
    • 一个数组

介绍

本篇文章主要是观看"灵茶山艾府"动态规划篇视频后,做出的笔记。
视频链接如下
[动态规划入门:从记忆化搜索到递推]
[0-1背包,完全背包]
[最长公共子序列,编辑距离]

动态规划入门:从记忆化搜索到递推

在这里插入图片描述

打家劫舍

对于第i间房有两种抉择,选或者不选。选的话对应的子问题就是前i-2间房,不选的话对应的子问题就是前i-1间房。从第一间房子或者最后一间房子考虑受到的约束最小。假设从最后一间房子开始考虑,形成的递归树如下
在这里插入图片描述

上述思考过程可以抽象为:

  • 当前操作?枚举i个房子选或者不选。
  • 子问题?从i个房子中得到的最大金额和
  • 下一个子问题:
    不选,从i-1个房子中得到的最大金额和
    选,从i-2个房子中得到的最大金额和

dfs(i) = max(dfs(i-1),dfs(i-1)+nums[i])
在定义dfs或者dp数组时,无法从单个元素中获取结果,而是从某一些元素中获取结果。

递归

class Solution {
public:int rob(vector<int>& nums) {//i表示第i件物品选还是不选//dfs(i)表示偷取前i家获得的最大金额int n=nums.size();return dfs(nums,n-1);}
private:int dfs(const vector<int>&nums,int i){if(i<0) return 0;return max(dfs(nums,i-1),dfs(nums,i-2)+nums[i]);}
};
  • 这份代码会超出时间限制。时间复杂度是指数级别的为O(2n)
  • 仔细观察上述的递归树就会发现有很多重复计算的地方,所以可以用一个数组用来存储计算过的值,这样下次直接拿来用即可。

在这里插入图片描述

记忆化递归

class Solution {
public:int rob(vector<int>& nums) {//i表示第i件物品选还是不选//dfs(i)表示偷取前i家获得的最大金额int n=nums.size();vector<int>cache(n,-1);return dfs(nums,n-1,cache);}
private:int dfs(const vector<int>&nums,int i,vector<int>&cache){if(i<0) return 0;//记忆化if(cache[i]!=-1) return cache[i];cache[i]=max(dfs(nums,i-1,cache),dfs(nums,i-2,cache)+nums[i]);return cache[i];}};
  • 时间复杂度=状态个数乘以单个状态花费的时间O(n*1)
  • 空间复杂度O(n)

递推

通过递归树和代码可以发现cache[i]的实际计算发生在向上"归"的过程中,所以我们直接省去向下"递"的过程只留下向上归的过程,这被称之为递推。

在这里插入图片描述

  • 自顶向下算=记忆化搜索
  • 自低向上算=递推

递归与递推

  • dfs->数组
  • 递归->循环
  • 递归边界->数组初始值
class Solution {
public:int rob(vector<int>& nums) {int n=nums.size();vector<int>f(n+2,0);for(int i=0;i<n;++i){f[i+2]=max(f[i+1],f[i]+nums[i]);}return f[n+1];}
};
  • 空间复杂度为O(n)
  • 时间复杂度为O(n)

滚动变量

class Solution {
public:int rob(vector<int>& nums) {int n=nums.size();int f0=0,f1=0,newf;for(int i=0;i<n;++i){newf=max(f1,f0+nums[i]);f0=f1;f1=newf;}return f1;}
};

背包

在这里插入图片描述

0-1 背包

在这里插入图片描述

递归写法

int zero_one(const vector<int>&w,const vector<int>&v,int i,int c){if(i<0) return 0;//不选if(c<w[i]) return zero_one(w,v,i-1,c);return max(zero_one(w,v,i-1,c),zero_one(w,i-1,c-w[i])+v[i]);}

记忆化递归

int zero_one(const vector<int>&w,const vector<int>&v,int i,int c,vector<vector<int>>&cache){if(i<0) return 0;if(cache[i][c]!=-1) return cache[i][c];//不选if(c<w[i]) cache[i][c]=zero_one(w,v,i-1,c);else cache[i][c]=max(zero_one(w,v,i-1,c),zero_one(w,v,i-1,c-w[i])+v[i]);return cache[i][c];}

目标和

假设所有元素中,正数的和为p,所有元素的总和是s,目标和为t,则所有负数的和就是(s-p),这样一来就可以得到一个等式p-(s-p)=t。移项之后可以得到p=(s+t)/2;由于p是正数之和故(s+t)的值一定大于0.要想一分为二(s+t)的值一定要是偶数才可以。

记忆化搜索

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {//p个正值,s-p个负值//p-(s-p)=t p=(t+s)/2for(auto&n:nums) target+=n;if(target<0 || target&1) return 0;target/=2;vector<vector<int>> cache(nums.size(),vector<int>(target+1,-1));return zero_one(nums,nums.size()-1,target,cache);}
private:int zero_one(const vector<int>&nums,int i,int target,vector<vector<int>>&cache){if(i<0) {//所有的数都选过一遍/*还有一个条件就是目标和需要满足target。又由于target是倒着减的,所以target==0的时候就找到了一种目标方案,此时返回1.*/if(target==0)  return 1;//找到一种方案return 0;}if(cache[i][target]!=-1) return cache[i][target];if(target<nums[i]) cache[i][target]=zero_one(nums,i-1,target,cache);else cache[i][target]=zero_one(nums,i-1,target,cache)+zero_one(nums,i-1,target-nums[i],cache);return cache[i][target];}             
};

递推

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {//p个正值,s-p个负值//p-(s-p)=t p=(t+s)/2for(auto&n:nums) target+=n;if(target<0 || target&1) return 0;target/=2;//递推int n=nums.size();vector<vector<int>> dp(n+1,vector<int>(target+1,0));dp[0][0]=1;   for(int i=1;i<=n;++i){for(int j=0;j<=target;++j){if(j<nums[i-1]) dp[i][j]=dp[i-1][j];else dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i-1]];}}return dp[n][target];}        
};
  • 时间复杂度为O(n2)。
  • 空间复杂度为O((n+1)*(target+1))
    时间复杂度是不能再优化了,那空间复杂度可以优化吗?

两个数组

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {//p个正值,s-p个负值//p-(s-p)=t p=(t+s)/2for(auto&n:nums) target+=n;if(target<0 || target&1) return 0;target/=2;//递推int n=nums.size();vector<vector<int>> dp(2,vector<int>(target+1,0));//vector<int> dp(target+1,0);dp[0][0]=1;//递归边界就是dp数组的初始值   for(int i=1;i<=n;++i){//滚动数组是直接在原有的状态上进行覆盖,所以需要倒着覆盖 for(int j=0;j<=target;++j){if(j<nums[i-1]) dp[i%2][j]=dp[(i-1)%2][j];else dp[i%2][j]=dp[(i-1)%2][j]+dp[(i-1)%2][j-nums[i-1]];}}return dp[n%2][target];}        };

一个数组

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {//p个正值,s-p个负值//p-(s-p)=t p=(t+s)/2for(auto&n:nums) target+=n;if(target<0 || target&1) return 0;target/=2;//递推int n=nums.size();vector<int> dp(target+1,0);dp[0]=1;   for(int i=1;i<=n;++i){//滚动数组是直接在原有的状态上进行覆盖,所以需要倒着覆盖for(int j=target;j>=nums[i-1];--j){dp[j]+=dp[j-nums[i-1]];}}return dp[target];}        };

这里解释一下,为什么第二层循环需要倒着遍历

在这里插入图片描述
如果是正着遍历,在i层的状态上修改i+1层的数据,
f[2] = f[2]+f[0]=3+1=4;
f[3] = f[3]+f[1]=5+2=7;
f[4] = f[4]+f[2]=6+4这里原本应该要加的是3,但是由于f[2]已经被覆盖了所以会变成加4,导致这里计算错误。

现在来看看倒着遍历会不会发生错误。
f[6] = f[6]+f[4] = 9+6=15;
f[5] = f[5]+f[3]= 7+5=12;
f[4] = f[4]+f[2] = 6+3=9;
f[3] = f[3]+f[1]= 5+2=7
f[2] = f[2]+f[0] = 3+1=4;
会发现这样计算不会出现错误。

完全背包

在这里插入图片描述

记忆化递归搜索

int dfs(const vector<int>&w,const vector<int> &v,int i,int c,vector<vector<int>>&cache){if (i<0){return  0;}if(cache[i][c]!=-1) return cache[i][c];if(c<w[i]) cache[i][c]=dfs(w,v,i-1,c,cache);else cache[i][c]=max(dfs(w,v,i-1,c,cache),dfs(w,v,i,c-w[i],cache)+v[i]);//i表示选过了还可以再选return cache[i][c];}
  • 与01背包类似,唯一不同的地方就是选中当前物品之后,下一个子问题还可以选取当前物品

零钱兑换

记忆化递归

class Solution {
public:int coinChange(vector<int>& coins, int amount) {int n=coins.size();vector<vector<int>> cache(n,vector<int>(amount+1,-1));int ans = dfs(coins,n-1,amount,cache);return ans==INT_MAX-1?-1:ans;}
private:int dfs(const vector<int>&ws,int i,int c,vector<vector<int>>&cache){if (i<0){if(c==0) return 0;return INT_MAX-1;}if(cache[i][c]!=-1) return cache[i][c];if(c<ws[i]) cache[i][c]=dfs(ws,i-1,c,cache);else cache[i][c]=min(dfs(ws,i-1,c,cache),dfs(ws,i,c-ws[i],cache)+1);return cache[i][c];}
};

递推

class Solution {
public:int coinChange(vector<int>& coins, int amount) {int n=coins.size();vector<vector<int>> dp(n+1,vector<int>(amount+1,INT_MAX-1));dp[0][0]=0;//vector<vector<int>> cache(n,vector<int>(amount+1,-1));for(int i=1;i<=n;++i){for(int j=0;j<=amount;++j){if(j<coins[i-1]) dp[i][j]=dp[i-1][j];else dp[i][j] = min(dp[i-1][j],dp[i][j-coins[i-1]]+1);}}return dp[n][amount]==INT_MAX-1?-1:dp[n][amount];}

背包问题变形[至多|恰好|至少]

在这里插入图片描述
这三种变形的不同在于它们的递归终止条件或者说动态规划的初始化条件
至多
在这里插入图片描述

在这里插入图片描述

恰好
在这里插入图片描述

在这里插入图片描述

至少
在这里插入图片描述

在这里插入图片描述

最长公共子序列

在这里插入图片描述
在这里插入图片描述

记忆化搜索

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {int l1=text1.size(),l2=text2.size();vector<vector<int>> cache(l1,vector<int>(l2,-1));return dfs(text1,text2,l1-1,l2-1,cache);}
private:int dfs(const string&s,const string&t,int i,int j,vector<vector<int>>&cache){if(i<0 || j<0) return 0;if(cache[i][j]!=-1) return cache[i][j];if(s[i]==t[j]) cache[i][j]=dfs(s,t,i-1,j-1,cache)+1;else cache[i][j]=max(dfs(s,t,i-1,j,cache),dfs(s,t,i,j-1,cache));return cache[i][j];}
};

递推

在这里插入图片描述

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {int l1=text1.size(),l2=text2.size();vector<vector<int>> dp(l1+1,vector<int>(l2+1,0));for(int i=1;i<=l1;++i){for(int j=1;j<=l2;++j){if(text1[i-1]==text2[j-1]) dp[i][j]=dp[i-1][j-1]+1;else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}return dp[l1][l2];}
};

两个一维数组

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {int l1=text1.size(),l2=text2.size();vector<vector<int>> dp(2,vector<int>(l2+1,0));for(int i=1;i<=l1;++i){for(int j=1;j<=l2;++j){if(text1[i-1]==text2[j-1]) dp[i%2][j]=dp[(i-1)%2][j-1]+1;else dp[i%2][j]=max(dp[(i-1)%2][j],dp[i%2][j-1]);}}return dp[l1%2][l2];}
};

一维数组

由递推公式可以看出,f[i][j]由f[i-1][j-1]、f[i-1][j]、f[i][j-1]三个状态得出。先假设只用一个数组完成三个状态的转换,会发现f[i-1][j-1]的状态在使用的时候已经被更新了,所以需要先保存之前的状态。
在这里插入图片描述

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {int l1=text1.size(),l2=text2.size();vector<int> dp(l2+1,0);for(int i=1;i<=l1;++i){int pre=dp[0];for(int j=1;j<=l2;++j){int tmp=dp[j];if(text1[i-1]==text2[j-1]) dp[j]=pre+1;else dp[j]=max(dp[j],dp[j-1]);pre=tmp;}}return dp[l2];}
};

编辑距离

在这里插入图片描述

记忆化搜索

class Solution {
public:int minDistance(string word1, string word2) {int l1=word1.size(),l2=word2.size();vector<vector<int>> cache(l1,vector<int>(l2,-1));return dfs(word1,word2,l1-1,l2-1,cache);}
private:int dfs(const string&w1,const string&w2,int i,int j,vector<vector<int>>&cache){if(i<0) return j+1;if(j<0) return i+1;if(cache[i][j]!=-1) return cache[i][j];if(w1[i]==w2[j]) cache[i][j]=dfs(w1,w2,i-1,j-1,cache);else cache[i][j]=min(dfs(w1,w2,i-1,j,cache),min(dfs(w1,w2,i,j-1,cache),dfs(w1,w2,i-1,j-1,cache)))+1;return cache[i][j];}
};

递推

class Solution {
public:int minDistance(string word1, string word2) {int l1=word1.size(),l2=word2.size();vector<vector<int>> dp(l1+1,vector<int>(l2+1,0));for(int i=1;i<=l2;++i) dp[0][i]=i;for(int i=1;i<=l1;++i) dp[i][0]=i;for(int i=1;i<=l1;++i){for(int j=1;j<=l2;++j){if(word1[i-1]==word2[j-1]) dp[i][j]=dp[i-1][j-1];else dp[i][j]=min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1;}}return dp[l1][l2];}
};
  • 时间复杂度为O(n*m)
  • 空间复杂度为O(n*m)

一个数组

class Solution {
public:int minDistance(string word1, string word2) {int l1=word1.size(),l2=word2.size();vector<int> dp(l2+1,0);//for(int i=1;i<=l2;++i) dp[0][i]=i;//for(int i=1;i<=l1;++i) dp[i][0]=i;for(int i=1;i<=l2;++i) dp[i]=i;for(int i=1;i<=l1;++i){int pre=dp[0];dp[0]+=1;//相当于二维数组中的dp[i][0]=i;for(int j=1;j<=l2;++j){int tmp=dp[j];if(word1[i-1]==word2[j-1]) dp[j]=pre;else dp[j]=min(dp[j],min(dp[j-1],pre))+1;pre=tmp;}}return dp[l2];}
};
  • 时间复杂度为O(n*m)
  • 空间复杂度为O(m)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/725662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序开发系列(二十)·wxml语法·setData()修改对象类型数据、ES6 提供的展开运算符、delete和rest的用法

目录 1. 新增单个、多个属性 1.1 新增单个属性 1.2 新增多个属性 2. 修改单个、多个属性 2.1 修改单个属性 2.2 修改多个属性 3. 优化 3.1 ES6 提供的展开运算符 3.2 Object.assign()将多个对象合并为一个对象 4. 删除单个、多个属性 4.1 删除单个属性 …

搬运机器人助力制造业转型升级

随着传统制造业的转型升级&#xff0c;智能化和多样化成为行业发展的新趋势。在这个过程中&#xff0c;富唯智能搬运机器人作为智能工厂的重要搬运机器人&#xff0c;展现出了卓越的性能和适应性。 它不仅能够应对各种材料、形状和重量的搬运装卸需求&#xff0c;还能与智能物理…

跨链桥的类型总结/相关的名词解释

首先&#xff0c;这是一个会持续更新的文章&#xff0c;我会不断把自己了解到的跨链桥名词解释更新在这里。 跨链桥类型 基于传输方式分类&#xff1a; Lock and Mint&#xff1a;在一条链上锁定资产&#xff0c;在另一条链上铸造等价资产liqidity pool&#xff1a;在不同链…

数字化转型导师坚鹏:金融科技咨询方法论

金融科技咨询方法论 ——方法、做法、演法、心法 课程背景&#xff1a; 数字化转型背景下&#xff0c;很多机构存在以下问题&#xff1a; 不知道先进的金融科技咨询方法论&#xff1f; 不知道如何运作金融科技咨询项目&#xff1f; 不知道如何汇报咨询项目关键成果&…

第五十一回 李逵打死殷天赐 柴进失陷高唐州-AI发展历程和常用框架

朱仝说只要杀了李逵就上梁山&#xff0c;柴进就劝李逵先在庄上住一段时间&#xff0c;先让朱仝、雷横和吴用回了梁山。 李逵在柴进庄上住了一个月&#xff0c;碰到柴进的叔叔柴皇城病重来信叫他去一趟&#xff0c;于是李逵就随着柴进去了高唐州。 柴皇城被殷天锡气死&#xf…

世界级通讯社发稿-法新社海外发稿渠道-大舍传媒

世界级通讯社发稿-法新社海外发稿渠道-大舍传媒 美联社&#xff1a;全球最大的通讯社之一 美联社&#xff08;Associated Press&#xff09;是全球最大的通讯社之一&#xff0c;成立于1846年&#xff0c;总部位于美国纽约。该社拥有一支庞大的全球新闻团队&#xff0c;涵盖了…

mysql8安装配置(最新版)

目录 一、下载mysql8 二、安装mysql8 三、配置mysql 一、下载mysql8 下载链接&#xff1a;https://pan.quark.cn/s/58d9072e51c4 二、安装mysql8 双击msi文件 选择custom 根据所需选择组件 修改安装路径 选中execute&#xff0c;安装&#xff0c;弹出提示安装VS的提示框之后…

MYSQL5.7报1205 - Lock wait timeout exceeded; try restarting transaction

简介 今天使用navicate操作添加时&#xff0c;mysql报错误&#xff0c;错误如下 原因 这个问题的原因是在mysql中产生了事务A&#xff0c;执行了修改的语句&#xff0c;比如&#xff1a; update t1 set aget18 where id1;此时事务并未进行提交&#xff0c;事务B开始运行&am…

自学高效备考2024年AMC10:2000-2023年1250道AMC10真题解析

我们今天继续来随机看5道AMC10真题&#xff0c;以及详细解析&#xff0c;这些题目来自1250道完整的官方历年AMC10真题库。通过系统研究和吃透AMC10的历年真题&#xff0c;参加AMC10的竞赛就能拿到好名次。 即使不参加AMC10竞赛&#xff0c;初中和高中数学一定会学得比较轻松、…

PTA L2-001 紧急救援

作为一个城市的应急救援队伍的负责人&#xff0c;你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候&#xff0c;你的任务是带领你的…

openGauss学习笔记-237 openGauss性能调优-SQL调优-SQL执行计划介绍-概述

文章目录 openGauss学习笔记-237 openGauss性能调优-SQL调优-SQL执行计划介绍-概述237.1 SQL执行计划概述237.1.1 执行计划显示信息 openGauss学习笔记-237 openGauss性能调优-SQL调优-SQL执行计划介绍-概述 237.1 SQL执行计划概述 SQL执行计划是一个节点树&#xff0c;显示o…

Redis核心数据结构之SDS和链表(三)

SDS与C字符串的区别 二进制安全 C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外&#xff0c;字符串里面不能包含空字符&#xff0c;否则最先被程序读入的空字符将被误认为是字符串结尾&#xff0c;这些限制使得C字符串只能保存文本数据&#xff0c;而…

Flyway 9.22.3 + springboot3 + MySQL8.0+,简单使用

文章目录 flyway的依赖配置ieda 启动&#xff01;&#xff01;&#xff01; 关于这篇文章主要是自己在使用flyway时遇到的一些问题以及最终的解决方法 当然包括所有的配置&#xff0c;主要目的是记录一下防止下次使用的时候忘记 flyway的依赖 这里 springboot 3 具体版本不再描…

#define MODIFY_REG(REG, CLEARMASK, SETMASK)

#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) 这个宏 MODIFY_REG 是在嵌入式编程中&#xff0c;它用于修改一个寄存器的特定位&#xff0c;而不影响其他位。这个宏接受三个参数&#xff…

中小企业如何降低网络攻击和数据泄露的风险?

德迅云安全收集了Bleeping Computer 网站消息&#xff0c; Arctic Wolf 表示 Akira 勒索软件组织的攻击目标瞄准了中小型企业&#xff0c;自 2023 年 3 月以来&#xff0c;该团伙成功入侵了多家组织&#xff0c;索要的赎金从 20 万美元到 400 多万美元不等&#xff0c;如果受害…

leetcode2

翻转链表 struct ListNode* reverseList(struct ListNode* head) {struct ListNode* prev NULL;struct ListNode* curr head;while (curr ! NULL) {struct ListNode* nextTemp curr->next;//nextTemp的作用是为了记录&#xff0c;以便再反转后可以curr->next prev; …

大数据开发-Hadoop之HDFS高级应用

文章目录 HDFS回收站HDFS的安全模式定时上传数据至HDFSHDFS的高可用和高扩展HDFS写数据过程源码剖析 HDFS回收站 HDFS会为每个用户创建一个回收站目录:/user/用户名/.Trash/回收站中的数据都会有一个默认的保存周期&#xff0c;过期未恢复则会被HDFS自动彻底删除默认情况下HDF…

数智驱动“坐商”变“行商”,安通控股迈向综合物流服务新高度

在全球经济增长放缓的大环境下&#xff0c;集装箱运输市场也面临供需双弱的新挑战。 国内内贸集装箱物流企业TOP3——安通控股股份有限公司&#xff08;以下简称“安通控股”&#xff09;激流勇进&#xff0c;积极推进营销理念从“业务操作型”向“客户营销型”转变、从传统的…

机器学习-启航

文章目录 原理分析机器学习的两种典型任务机器学习分类总结数据机器学习分类解读简单复杂 原理分析 马克思主义哲学-规律篇 规律客观存在&#xff0c;万事万物皆有规律。 机器学习则是多维角度拆解分析复杂事实数据&#xff0c;发现复杂事实背后的规律&#xff0c;然后将规律用…

C++小记 -链表

链表 文章目录 链表链表基础理论链表的类型单链表双链表循环链表 链表的存储方式链表的定义链表的操作添加节点删除节点 性能分析构建链表删除节点&#xff08;内存泄漏的坑&#xff09;1.直接移除2.使用虚拟头结点3.delete指针后&#xff0c;要将指针置为NULL&#xff01;&…