力扣刷题笔记——动态规划

news2024/12/21 22:08:18

 动态规划基础

 简称DP如果某⼀问题有很多重叠⼦问题,使⽤动态规划是最有效的。

 动态规划中每⼀个状态⼀定是由上⼀个状态推导出来的
1. 确定dp 数组( dp table )以及下标的含义
2. 确定递推公式
3. dp 数组如何初始化
4. 确定遍历顺序
5. 举例推导 dp 数组

509. 斐波那契数

509. 斐波那契数 - 力扣(LeetCode)https://leetcode.cn/problems/fibonacci-number/

 首先思考dp数组的含义:

dp[i]是指数组中第i个数的值

状态转移方程:dp[i]=dp[i-1]+dp[i-2]

初始化:初始第1和第2个数

确定遍历顺序从头开始到第n个

举例推导dp[i] = dp[i - 1] + dp[i - 2]
class Solution {
public:
 int fib(int N) {
     if (N <= 1) return N;
     vector<int> dp(N + 1);
     dp[0] = 0;
     dp[1] = 1;
     for (int i = 2; i <= N; i++) {
     dp[i] = dp[i - 1] + dp[i - 2];
     }
     return dp[N];
}
};

70.爬楼梯 

70. 爬楼梯 - 力扣(LeetCode)https://leetcode.cn/problems/climbing-stairs/

首先思考dp数组得含义:

dp[i]是指爬到第i阶有几种方法

dp[i]=dp[i-1] + dp[i-2]

初始化:初始第1和第2个数

确定遍历顺序从头开始到第n个

举例推导dp[i] = dp[i - 1] + dp[i - 2]
class Solution {
public:
    int climbStairs(int n) {

        if(n<=2){
            return n;   
        }
        vector<int> vec(n+1,0);
        vec[1]=1;
        vec[2]=2;
        for(int i=3;i<=n;i++){
            vec[i]=vec[i-1]+vec[i-2];
        }        

        return vec[n];
    }
};

746. 使用最小花费爬楼梯


746. 使用最小花费爬楼梯 - 力扣(LeetCode)https://leetcode.cn/problems/min-cost-climbing-stairs/submissions/

dp[i]表示到达第i个台阶最小的花费

dp[i]=min(dp[i-1],dp[i-2])+cost[i];

初始化dp[0]=cost[0] dp[1]=cost[1]

顺序是从第2个台阶开始 

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        vector<int> dp(cost.size(),0);    
        //初始化数组
        dp[0]=cost[0];
        dp[1]=cost[1];
        for(int i=2;i<cost.size();i++){
            dp[i]=min(dp[i-1],dp[i-2])+cost[i];
        }
        return min(dp[cost.size()-1],dp[cost.size()-2]);
    }
};

 62.不同路径

62. 不同路径 - 力扣(LeetCode)https://leetcode.cn/problems/unique-paths/

本题需要记录每个位置的可以到达的路径的数目

dp[i][j]表示到达第i行第j列的点的路径

dp[i][j]=dp[i-1][j]+dp[i][j-1] 每一个位置,可以通过上面和左边的位置前进一步得到

初始化明显dp[i][0]=1 dp[0][j]=1 表示第一行和第一列的到达的路径都为1

遍历的顺序从每层一层一层遍历

class Solution {
public:
int uniquePaths(int m, int n) {
	vector<vector<int>>  dp(m, vector<int>(n, 1));
    
	for (int i = 1; i < m; i++) {
		for (int j = 1; j < n; j++) {
			dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
		}
	}

	return dp[m - 1][n - 1];
}
};

 63.不同的路径II

63. 不同路径 II - 力扣(LeetCode)https://leetcode.cn/problems/unique-paths-ii/

 本体相对于上一题,中间多了路障

在初始化地图时,我们需要将有障碍的地方识别出来

对于每一个点,都可以通过上面或者左边的节点过来

状态转移方程:grid[i][j]=grid[i-1][j]+grid[i][j-1]

初始化:第一行和第一列每一个元素初始化为1

顺序:每一行不停的向下遍历

最后返回grid[obstacledGrid.size()][obstacleGrid[0].size()]

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int n=obstacleGrid.size();
        int m=obstacleGrid[0].size();
        vector<vector<int>>  grid(n,vector<int>(m,0));
        //初始化数组 第一行和第一列可以到达的路径都是1
        for(int i=0;i<n&&obstacleGrid[i][0]==0;i++) grid[i][0]=1;
        for(int j=0;j<m&&obstacleGrid[0][j]==0;j++) grid[0][j]=1;

        for(int i=1;i<n;i++){
            for(int j=1;j<m;j++){
                if(obstacleGrid[i][j]==1) continue;
                grid[i][j]=grid[i-1][j]+grid[i][j-1];
            }
        }


        return grid[n-1][m-1];
    }
};

 343 整数拆分

343. 整数拆分 - 力扣(LeetCode)https://leetcode.cn/problems/integer-break/

dp[i]表示i拆分的最大的乘积

递推公式(状态转移公式):遍历之前的dp数据 dp[i]=max(dp[i],max(dp[i-j]*j,j*(i-j)));

dp[i]很小(起一个迭代的作用),因此取最大值基本都是后一个max中的值(dp[i-j]表示i-j拆分成的最大乘积,如果这个树乘以j大,那么不断循环,到i-1的情况,就可以找出i拆分的最大的值)

初始化,对于0和1没法拆分,那么最大的乘积为0

顺序:从1开始,从前往后

class Solution {
public:
    int integerBreak(int n) {
        vector<int> dp(n+1,0);
        dp[2]=1;
        for(int i=3;i<=n;i++){
            for(int j=1;j<i-1;j++){
                dp[i]=max(dp[i],max(dp[i-j]*j,j*(i-j)));
            }
        }
        return dp[n];
    }
};

96.不同的二叉搜索树 

96. 不同的二叉搜索树 - 力扣(LeetCode)https://leetcode.cn/problems/unique-binary-search-trees/

dp[n] 一维的数组,思考含义i表示i个元素可以构成的二叉搜索树的个数

状态转移:dp[i]+=dp[j-1][i-j] (j的范围为1到i)

初始化思考i为0的情况:0个元素构成的搜索树可以直接看成一个二叉搜索树dp[0]=1

顺序:从第一个节点开始,直到第n个节点

返回 dp[n];

 为什么是乘法呢,因为左右子树相当于不同的选择步骤

因此,最终的结果的个数应该是两者的乘积

class Solution {
public:
    int numTrees(int n) {
        
        //dp[i]为i个节点时,二叉搜索树的中枢
        vector<int> dp(n+1);
        dp[0]=1;
        
        for(int i=1;i<=n;i++){
            for(int j=1;j<=i;j++){
                dp[i]+=dp[j-1]*dp[i-j];
            }
        }

        return dp[n];
    }

};

背包问题

背包问题是动态规划经典的问题,包含01背包,完全背包,多重背包等子问题

0-1背包问题

N 件物品和⼀个最多能被重量为 W 的背包。第 i 件物品的重量是 weight[i] ,得到的价值是
value[i] 每件物品只能⽤⼀次 ,求解将哪些物品装⼊背包⾥物品价值总和最⼤。
总结一下,就是把给定的物品向背包里装,求出能够装进去的最大价值

 定义一个数组int dp[i][j] ,i和物品的数量一样 j为背包的最大容量

dp[i][j] 为在容量为j的情况下,前i个物品合理装进去能够获得的最大的价值

转移公式: dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);

初始化:对于j取j<value[0]是为0,如果j>value[0],则为value[0];

int main(){
    vector<vector<int>> dp(weight.size()+1,vector<int>(rongliang+1,0));
    //全部初始化为0
    for(int j=value[0];j<=rongliang;j++){
        dp[0][j]=value[0];
    }
    for(int i=1;i<weight.size();i++){
        for(int j=0;j<=rongliang;j++){
            if(j<weight[i]) dp[i][j]=dp[i-1][j];
            else
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j-weight[i]] + value[i]);    
        }
    }

    cout << dp[weight.size() - 1][bagWeight] << endl;
}
#include<iostream>
#include<vector>

using namespace std;

int main() {
	int sum, rongliang;
	cin >> sum >> rongliang;
	vector<int>  tiji(sum+1, 0);
	vector<int>  jiazhi(sum+1, 0);
	for (int i = 1; i <= sum; i++) {
		cin >> tiji[i] >> jiazhi[i];
	}

	//初始化dp数组
	vector<vector<int>> dp(sum + 1, vector<int>(rongliang + 1, 0));

	//进行动态规划
	for (int i = 1; i <= sum; i++) {
		for (int j = rongliang; j >= 0; j--) {
			//状态转移方程
			if (j >= tiji[i]) {
				//装得下
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - tiji[i]] + jiazhi[i]);
			}
			else {
				//装不下
				dp[i][j] = dp[i - 1][j];
			}
		}
	}
	cout << dp[sum][rongliang];
	return 0;
}

这两份代码不同:

第一:价值和重量数组的起始位置不同

第二:在第二轮循环中,第二个程序从大到小

起始位置不同,导致了初始化的过程不同,第二个不需要初始化,初始化化的过程包含在循环中

因为dp的值都是从上一行的状态转移过来的,那么就是说我们不要在意遍历的顺序,那么从开始到最后,从最后到开始都是一样的,我们依旧可以从容量开始,不断递减

思考如何简化问题的求解

使用一位动态数组就可以解决问题

dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);

如果说把dp[i-1]的那一层拷贝到i层,表达式就可以是dp[i][j]=max(dp[i][j],dp[i][j-weight[i]]+value[i]);

只是用一维数组,只使用dp[j]

416.分割等和子集

416. 分割等和子集 - 力扣(LeetCode)https://leetcode.cn/problems/partition-equal-subset-sum/

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        
        int sum=0;
        vector<int> dp(10001,0);
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
        }
        if(sum%2!=0) return false;
        int target=sum/2;
        
        for(int i=0;i<nums.size();i++){
            for(int j=target;j>=nums[i];j--){
                dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        } 


        if(dp[target]==target) return true; 
        return false;
    }
};

 分割子集,找到和相同的子集

分半,找到容量最大的;

dp数组含义,容量为i时,取得数组元素和的最大值

dp[i]=max(dp[i],dp[i-nums[i]]+nums[i])

初始化,当容量小于最小的数组元素时,dp为零,因此初始化为全零

1049. 最后一块石头的重量II

1049. 最后一块石头的重量 II - 力扣(LeetCode)https://leetcode.cn/problems/last-stone-weight-ii/

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum=0;
    
        for(int i=0;i<stones.size();i++){
            sum+=stones[i];
        }
        vector<int> vec(150001,0);
        int target=sum/2;

        for(int i=0;i<stones.size();i++){
            for(int j=target;j>=stones[i];j--){
                vec[j]=max(vec[j],vec[j-stones[i]]+stones[i]);
            }
        }

        return sum-vec[target]-vec[target];
    }
};

思考本题:
         总数分半,获得容量最大可能性

        sum-dp[target]-dp[target];

        sum-dp[target]一定大于dp[target] 

494、目标和 

494. 目标和 - 力扣(LeetCode)https://leetcode.cn/problems/target-sum/

在数字前面加+-号得到目标和的数

就是装够特定容量大小的东西,总共有几种装法

dp[j]+=dp[j-nums[i]] 

遍历的顺序和01背包问题一样

初始化:通过状态转移方程可以得出,当为0时,代表结果为0的时候有几种方案

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {

        int sum=0;
        for(auto i:nums){
            sum+=i;
        }
        if(target>sum) return 0;
        if((target+sum)%2==1) return 0;

        int nice=(target+sum)/2;

        vector<int> vec(1001,0);
        vec[0]=1;
        for(int i=0;i<nums.size();i++){
            for(int j=nice;j>=nums[i];j--){
                vec[j]+=vec[j-nums[i]];
            }
        }
        return vec[nice];
    }
};

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

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

相关文章

深度学习的分割方法

FCN&#xff1a;基于深度学习的语义分割模型 语义分割的定义&#xff1a;对像素进行精细化的分类。 用深度学习来解决语义分割&#xff0c;所面临的主要问题是&#xff1a; 早期的深度模型用于分类&#xff0c;输出一维向量&#xff0c;无法分割 深度模型不够精细 动机 如…

谈谈互联网广告拍卖机制的发展:从GSP到DeepAuction

广告作为各互联网公司收入的大头&#xff0c;其拍卖机制设计因此也是关乎营收最为核心的方面。所谓的广告拍卖机制设计是指如何将有限的广告位分配给合适的广告&#xff0c;从而达到客户、平台以及用户三方的价值最优。 当前的广告拍卖被建模为暗拍的形式&#xff0c;即N个广告…

ROS:launch启动文件的使用方法

目录 一、launch文件结构二、launch文件语法2.1根元素2.2参数设置2.3重映射、嵌套 三、示例3.1示例一3.2示例二3.3示例三3.4示例四 一、launch文件结构 由XML语言写的&#xff0c;可实现多个节点的配置和启动。 不再需要打开多个终端用多个rosrun命令来启动不同的节点了 可自动…

Swift 周报 第三十期

文章目录 前言新闻和社区App、App 内购买项目和订阅即将实行税率调整码出新宇宙Apple 公证服务更新Apple 设计大奖入围名单公布 提案通过的提案 Swift论坛推荐博文话题讨论关于我们 前言 本期是 Swift 编辑组自主整理周报的第二十一期&#xff0c;每个模块已初步成型。各位读者…

C++进阶 —— C++11新增容器

目录 一&#xff0c;array 二&#xff0c;forward_list 三&#xff0c;unordered unordered_set unordered_multiset unordered_map unordered_multimap 静态数组array、forward_list、unordered系列&#xff1b; 一&#xff0c;array array是固定大小尺寸的序列容器&am…

2023年6月东莞/惠州/深圳CPDA数据分析师认证招生

CPDA数据分析师认证是大数据方面的认证&#xff0c;助力数据分析人员打下扎实的数据分析基础知识功底&#xff0c;为入门数据分析保驾护航。 帮助数据分析人员掌握系统化的数据分析思维和方法论&#xff0c;提升工作效率和决策能力&#xff0c;遇到问题能够举一反三&#xff0c…

100天精通Golang(基础入门篇)——第1天:学习Go语言基本概念

&#x1f337; 博主 libin9iOak带您 Go to Golang Language.✨ &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &#x1f30a; 《I…

实战:Spring Cloud Stream消息驱动框架整合rabbitMq

文章目录 前言Spring Cloud Stream简析Spring Cloud Stream与rabbitmq整合1、添加pom依赖2、application.yml增加mq配置3、定义输入输出信道4、使用输入输出信道收发消息5、模拟正常消息消费6、模拟异常消息 前言 相信很多同学都开发过WEB服务&#xff0c;在WEB服务的开发中一…

Web端3D模型轻量化工具如何实现建筑行业“数字化”建设?

随着数字化技术的飞速发展&#xff0c;建筑行业也在不断寻找新的技术手段来提供高产能和建筑质量。其中&#xff0c;Web端3D模型轻量化工具HOOPS Communicator SDK在建筑行业中的应用不断地得到了市场的广泛注意和应用。本文将深入探讨HOOPS Communicator在建筑行业中的应用及其…

转动的车轮-第14届蓝桥杯国赛Scratch真题初中级组第2题

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第144讲。 转动的车轮&#xff0c;本题是2023年5月28日上午举行的第14届蓝桥杯国赛Scratch图形化编程初中级组真题第2题…

Python数据攻略-DataFrame的数据操作

大家好&#xff0c;我是Mr数据杨&#xff0c;今天我们就来谈谈Python中的数据访问和修改。 首先&#xff0c;你们一定都听过《三国演义》吧&#xff0c;里面的人物和事情其实就像我们Python中的数据。比如曹操就像我们的数据元素&#xff0c;他的性格特点、军事才能等就像我们…

5年开发经验,看完这份37W字Java高性能架构,终于拿到架构师薪资

其实现在很多的开发人员并不能解决从架构的角度全方位地了解在Java编程过程中各阶段会出现的典型问题&#xff0c;更没办法深入到底层原理了解问题出现的原因&#xff01; 且随着当下面试越来越深入到底层&#xff0c;如果大家对于底层的原理不了解的话&#xff0c;是很难做出…

JDK11+mybatis-plus+shardingsphere分库分表

1、引入jar dynamic-datasource-spring-boot-starter&#xff1a;2.5.6 sharding-jdbc-spring-boot-starter&#xff1a;4.1.1 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId>&…

微调样本质量胜于数量 LIMA: Less Is More for Alignment

1、总体介绍 大型语言模型的训练分为两个阶段&#xff1a;&#xff08;1&#xff09;从原始文本中进行无监督的预训练&#xff0c;以学习通用的表征&#xff1b;&#xff08;2&#xff09;大规模的指令学习和强化学习&#xff0c;以更好地适应最终任务和用户的偏好。 作者通过…

作为网络安全工程师,都有哪些公司可以选?

招聘平台 首选内推 其次是公司自有招聘平台 再是第三方平台&#xff1a;boos直聘、前程无忧、拉钩、猎聘、牛客、牛聘 乙方 启明星辰 商标&#xff1a;云众可信&#xff0c;云子可信 投资&#xff1a;网御星云&#xff0c;恒安嘉新 拳头产品&#xff1a;Secin 社区、天清…

企企通×天能股份SRM一期项目成功上线,持续深化企业采购数字化

近期&#xff0c;企企通凭借在赋能客户数字化转型方面的优秀实践与丰富的解决方案&#xff0c;荣获天能电池集团股份有限公司&#xff08;以下简称“天能股份”&#xff09;颁发的“2022年度数字化优秀供应商奖”&#xff0c;同时&#xff0c;企企通SRM项目还获得天能股份采购管…

vue-cli4打包优化

项目开始时webpack配置 vue-cli3以后&#xff0c;我们修改webpack配置&#xff0c;需要自己在项目根路径下创建vue.config.js文件。 一、 配置 proxy 跨域 使用vue-cli发开项目&#xff0c;在本地开发环境中&#xff0c;如果遇到跨域的问题。可以通过配置proxy的方式&#xff…

uniapp(二) 之 uniapp 搭建与组件库的引用

小扩展&#xff1a; rpx&#xff08;responsive pixel&#xff09;:可以根据屏幕宽度自适应。规定屏幕宽度为750rpx。如果iphon6上&#xff0c;屏幕宽度为375px,共有750个像素&#xff0c;则750rpx 375培训 750物理像素&#xff0c;1rpx 0.5px 1物理像素。 页面跳转&#xff…

你知道TikTok的推荐算法吗?TikTok数据分析平台哪家好?

作为当下最受欢迎的社交媒体&#xff0c;TikTok这几年的成绩大家也是有目共睹了&#xff0c;超10亿的月活加上大量活跃的年轻人&#xff0c;让无数企业和品牌为之心动。入局的人越来越多&#xff0c;想要在众多竞争者中脱颖而出&#xff0c;入局前需要了解TikTok底层逻辑和推荐…

Treadlocal源码实例详解

我们都知道treadlocal维护变量时候&#xff0c;可以为每个线程维护一个独立的副本&#xff0c;改变的是自己线程的数据。 ThreadLocal公用方法有四个&#xff1a;get&#xff0c;set&#xff0c;remove&#xff0c;intiValue 既然threadLocalMap是局部变量&#xff0c;所以他存…