}
2.2 爬楼梯
70. 爬楼梯
dp:
-
爬到第i层楼梯,有dp[i]种方法
-
dp[i] = dp[i-1] + dp[i-2] 从i-1上来和从i-2上来
-
dp[1] = 1,dp[2] = 2
-
从左往右
class Solution {
public int climbStairs(int n) {
if(n <=2) return n;
int[] dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
for(int i = 3;i <= n;i++) dp[i] = dp[i-1] + dp[i-2];
return dp[n];
}
}
2.3 使用最小花费爬楼梯
746. 使用最小花费爬楼梯
dp:
-
爬到第i层,最小花费
-
dp[i] = min(dp[i-1],dp[i-2]) + cost[i]
-
dp[0] = cost[0],dp[1] = cost[1]
-
从左往右
class Solution {
public int minCostClimbingStairs(int[] cost) {
int n = cost.length;
int[] dp = new int[n+1];
dp[0] = cost[0];
dp[1] = cost[1];
for(int i = 2;i < n;i++) dp[i] = Math.min(dp[i-1],dp[i-2]) + cost[i];
return Math.min(dp[n-1],dp[n-2]);
}
}
2.4 不同路径
62. 不同路径
dp:
-
dp[i] [j],从起点到 (i,j) 有多少种方案
-
dp[i] [j] = dp[i-1] [j] + dp[i] [j-1]
-
dp[0] [0] = 0 dp[01] = 1 dp[1 0] = 1
-
从左上到右下
class Solution {
public int uniquePaths(int m, int n) {
// dp[i][j] = dp[i-1][j]+dp[i][j-1]
int[][] dp = new int[m][n];
dp[0][0] = 0;
// 最左一列
for(int i = 0;i < m;i++) dp[i][0] = 1;
// 最上一行
for(int j = 0;j < n;j++) dp[0][j] = 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];
}
}
爆搜:
class Solution {
public int uniquePaths(int m, int n) {
return dfs(1,1,m,n);
}
private int dfs(int i,int j,int m,int n){
if(i == m && j == n) return 1;
// 越界
else if(i > m || j > n) return 0;
return dfs(i+1,j,m,n) + dfs(i,j+1,m,n);
}
}
数论:
63. 不同路径 II
dp:
-
dp[i] [j] 从起点到(i,j)有多少种方案
-
if 不是障碍物 dp[i] [j] = dp[i] [j-1] + dp[i-1] [j]
-
dp[0] [0] = 0 出现障碍物前 dp[i,0] = 1,dp[0,j] = 1,之后为0
-
从左上到右下,即一行一行的遍历,因为只会用到自己上面一行和左边的数字
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MtI6QvCe-1637219477435)(https://i.loli.net/2021/11/15/RDY8QkEIqC2gzSB.png)]
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int n = obstacleGrid.length;int m = obstacleGrid[0].length;
int[][] dp = new int[n][m];
// dp[i][j] = dp[i-1][j] + dp[i][j-1]
// 初始化
// 最左一列和最右一行有一个出现了障碍物,那么之后的都为0
for(int i = 0;i < n && obstacleGrid[i][0] != 1;i++) dp[i][0] = 1;
for(int j = 0;j < m && obstacleGrid[0][j] != 1;j++) dp[0][j] = 1;
// 当前不是障碍物就正常处理,否则为0
for(int i = 1;i < n;i++){
for(int j = 1;j < m;j++){
if(obstacleGrid[i][j] != 1) dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[n-1][m-1];
}
}
343.整数拆分
343. 整数拆分
dp:
-
dp[i],将数值i拆分,得到的最大乘积
-
dp[i] = max( dp[i] ,max( dp[j-i] * j , j * ( j - i ) ))
-
dp[2] = 1
-
从左往右
class Solution {
public int integerBreak(int n) {
// dp[i] 拆分数字i,可以得到最大乘机为dp[i]
// j*(i-j) jdp[i-j] j(i-j) 是将整数拆分成两个数 j*dp[i-j]是拆分成两个及两个以上
// dp[i] = max(dp[i],max(jdp[i-j],j(i-j)))
// dp[2] = 1
int[] dp = new int[n+1];
dp[2] = 1;
for(int i = 3;i <= n;i++){
for(int j = 1;j < i ;j++){
dp[i] = Math.max(dp[i],Math.max(jdp[i-j],j(i-j)));
}
}
return dp[n];
}
}
96.不同的二叉搜索树
96. 不同的二叉搜索树
class Solution {
public int numTrees(int n) {
// dp[i] 有i个节点组成,且1到i互不相同的二叉搜索树的个数
// dp[i] += dp[j-1]*dp[i-j]
// dp[3] = 元素1为头结点搜索树的数量 + 元素2为头结点搜索树的数量 + 元素3为头结点搜索树的数量
// 元素1为头结点搜索树的数量 = 左子树0个元素,右子树2个元素
// 元素2为头结点搜索树的数量 = 左子树1个元素,右子树1个元素
// 元素3为头结点搜索树的数量 = 左子树2个元素,右子树0个元素 左小右大
// dp[2] = 2个节点组成,且互补相同
// dp[1] = 1个节点组成,且互不相同
// dp[3] = dp[0]*dp[2] + dp[1]*dp[1] + dp[2]*dp[0]
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2;i <= n;i++){
// 第i个节点需要考虑从1~i作为根节点的情况
// 一共i个节点,j为首节点,左边有j-1个节点,右边有i-j个节点
for(int j = 1;j <= i;j++) dp[i] += dp[j-1]*dp[i-j];
}
return dp[n];
}
}
0-1背包问题
有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ygU60Oyw-1637219477437)(https://i.loli.net/2021/11/16/DCxmIl3oZdbTQt5.jpg)]
0-1背包问题
二维:
-
dp[i j] 从0~i 中任选、背包容量为j,最大价值
-
dp[i j] = max(dp[ i-1 , j ],dp[i - 1 , j - weight[i] ] + value[i])
-
dp[i 0] 容量为0的都为0,还有最上面一行,能放下第一个物品后的价值都为value[1]
-
由左上推出右下
一维:
-
dp[j] 容量为 j ,最大价值
-
dp[ j ] = max(dp[ j ] , dp[ j - weight[i] ] + value[i])
-
dp[0] = 0
-
滚动数组,右边的从上一伦的左边推出来
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
// 重量、价值
int weight[N],value[N];
// 个数,容量
int n,bag;
void m1();
void m2();
int main(){
scanf(“%d%d”,&n,&bag);
for(int i = 0;i < n;i++) scanf(“%d%d”,&weight[i],&value[i]);
// m1();
m2();
}
void m2(){
// dp[j] 容量为j的背包,所背的物品价值可以最大为dp[j]。
vector dp(bag+1,0);
// dp[j] = max(dp[j],dp[j-weight[i]]+value[i])
// dp[j] 不取物品 dp[j-weight[i]+value[i] 取第i件物品
// 初始化
// dp[0] = 0
dp[0] = 0;
for(int i = 0;i < n;i++){
// 为什么是倒叙?
// 因为一维dp是滚动数组,其实就是将i-1行复制到第i行
// 第i行依赖于第i-1行的正上方或者是左上方,如果
// 左边更新了,会影响到右边
// 对于每次新的一行,更新右边的值,其实是用上一行的左边的值
// j 要大于 weight[i] 要不然就是背包容量小于 物品的重量了
for(int j = bag;j >= weight[i];j–) dp[j] = max(dp[j],dp[j-weight[i]] + value[i]);
}
cout << dp[bag] << endl;
// for(int i = 0;i <= bag;i++) cout << dp[i] << " ";
}
void m1(){
// dp[i][j] 在0~i件物品中任选,背包容量为j,dp[i][j]是最大价值
// dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])
// dp[i-1][j]表示不要第i件物品,dp[i-1][j-weight[j]]+value[i]表示要第i件物品
vector<vector> dp(n,vector(bag+1,0));
dp[0][0] = 0;
// 最右边一列是0
// for(int i = 0;i < n;i++) dp[i][0] = 0;
// 最上面一列只有能装下第一件物品才有价值
for(int j = weight[0];j <= bag;j++) dp[0][j] = value[0];
for(int i = 1;i < n;i++){ // 遍历物品,第0件不用遍历了
for(int j = 0;j <= bag;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[n-1][bag] << endl;
}
416. 分割等和子集
-
dp[i] 容量为i,能放下的最大价值为dp[i]
-
dp[ i ] = max(dp[i],dp[i-weight[i]]+value[i])
-
dp[0] = 0
-
滚动数组,右边的从上一伦的左边推出来
class Solution {
public boolean canPartition(int[] nums) {
// 背包体积为sum/2
// 背包的商品的重量是商品的数值、价值也是元素的数值
// 可以分割表示背包刚好装满
// dp[i] 容量为i,最大凑成的i的子集
// dp[i] = max(dp[i],dp[i-nums[i]]+nums[i])
int n = nums.length,target;
int sum = 0;
for(int i = 0;i < n;i++) sum += nums[i];
// sum不是偶数,凑不出来
if(sum % 2 != 0) return false;
target = sum / 2;
// 容量为target
int[] dp = new int[target+1];
dp[0] = 0;
for(int i = 0;i < n;i++){
for(int j = target;j >= nums[i];j–) dp[j] = Math.max(dp[j],dp[j-nums[i]]+nums[i]);
}
// 背包容量是sum/2,并且最大能装sum/2
// dp[i] <= i 永远成立
// i是容量 dp[i]是容量为i的时候,能装进去的最大值
// 物品的重量 = 物品的质量 = 物品的数值
return dp[target] == target;
}
}
第二次写总结:
class Solution {
public boolean canPartition(int[] nums) {
int sum = 0,n = nums.length,target;
for(int i = 0;i < n;i++) sum += nums[i];
if(sum % 2 != 0) return false;
else target = sum / 2;
// 现在就是找nums中能否组合出一个和为target的组合
// 并且每个元素只能用一次
// dp[j] 容量为j,凑成最大的字节和
// dp[j] = max(dp[j],dp[j-nums[i]]+nums[i])
// 很类似以0-1背包问题,就是一堆物品,一个容量,看最多能装的多大价值的东西
// 只是这里的重量和价值是一样的而已
int[] dp = new int[target+1];
for(int i = 0;i < n;i++){ // 遍历物品
for(int j = target;j >= nums[i];j–) dp[j] = Math.max(dp[j],dp[j-nums[i]]+nums[i]);
}
// dp[target] 容量为 target,看最大能不能凑出 target
// 不能的话就没希望了,因为dp[j] <= j
return dp[target] == target;
}
}
1049. 最后一块石头的重量 II
class Solution {
public int lastStoneWeightII(int[] stones) {
// 本题其实就是将石头分成两份最接近的,然后剩下的就是答案
// dp[i] 容量为i,最多能装dp[i]重量的石头
// 石头共重sum,那么一半是target
int n = stones.length,sum = 0,target;
for(int i = 0;i < n;i++) sum += stones[i];
target = sum / 2;
// target是sum/2向下取整
// 背包容量是石头总重的一半,看最大能装多重
int[] dp = new int[target+1];
dp[0] = 0;
for(int i = 0;i < n;i++){
for(int j = target;j >= stones[i];j–) dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i]);
}
// 此时一堆石头是 dp[target],另外一堆是 sum - dp[targer]
// dp[target] <= target 所以 sum - dp[targer] >= dp[target]
return sum - dp[target] - dp[target];
}
}
494. 目标和
class Solution {
public int findTargetSumWays(int[] nums, int target) {
// nums 可以分为两部分、一部分做正数 a、一部分做负数 b(b还是一个正数,前面整体加一个符号就是负数了)
// target = a - b
// sum = a + b -> b = sum - a
// target = a - sum + a -> a = (target + sum ) / 2;
// a 是 nums中的一部分数之和
// 能在 nums中找到a,那么b自然就能找到
// 使用0-1背包来思考,dp[a],填满a这么大容积的背包,有dp[a]种方法
int n = nums.length, sum = 0;
for(int i = 0;i < n;i++) sum += nums[i];
// dp[i] 填满i这么大容积的背包、由dp[i]种方法
// dp[j] += dp[j-num[i]]
// 搞满容量为3的背包,有dp[3]种方法
// 如果 num[i] = 2,那么至少dp[5]有dp[3]种
// dp[0] = 1 装满0,只有一种,装0
// target比全是正数还大
if(Math.abs(target) > sum) return 0;
// 按照上面的推倒、a应该是偶数
if((target + sum) % 2 == 1) return 0;
int a = (target + sum) / 2;
int[] dp = new int[a+1];
for(int i = 0;i < n;i++){
for(int j = a;j >= nums[i];j–) dp[j] += dp[j-nums[i]];
}
return dp[a];
// 在求装满背包有几种方法的情况下,递推公式一般为:
// dp[j] += dp[j - nums[i]];
}
}
我感觉这几题都在做一件事:从一个序列里面找和为target的、可能是能否为target、可能是为target有多少种。
474. 一和零
重量有两个维度的0-1背包问题
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
// dp[i][j] 最多有i个0,j个1的strs的最大子集的大小为dp[i][j]
// dp[i][j] = max(dp[i][j],dp[i-zeroNum][j-oneNum] + 1);
// dp[0][0] = 0
// 这个其实不是二维dp,是物品的重量有两个维度,[i][j]一起构成j
// 字符串的zeroNum和oneNum相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i])
int[][] dp = new int[m+1][n+1];
for(String str : strs){ // 遍历物品
int zeroNum = 0,oneNum = 0;
for(int i = 0;i < str.length();i++){
if(str.charAt(i) == ‘0’) zeroNum++;
else oneNum++;
}
for(int i = m;i >= zeroNum;i–){
for(int j = n;j >= oneNum;j–) dp[i][j] = Math.max(dp[i][j],dp[i-zeroNum][j-oneNum]+1);
}
}
return dp[m][n];
}
}
完全背包问题
有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。
完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。
01背包
for(int i = 0;i < n;i++){ // 遍历商品个数
// 反向遍历
for(int j = bag;j >= weight[i];j–) dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?
掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-Jl64yxLF-1713551169416)]
[外链图片转存中…(img-Xatlbj9M-1713551169417)]
[外链图片转存中…(img-SRIZhcgA-1713551169417)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?
掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。
[外链图片转存中…(img-Qhkmxwd5-1713551169417)]
[外链图片转存中…(img-99KHihn9-1713551169418)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!