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

leetcode day 35 01背包问题 416+1049

0-1背包问题

(1)第一种情况:二维dp[i][j]数组

dp[i][j]表示[0,i]的物品放入容量为j背包的最大价值

不放物品i,dp[i][j]=dp[i-1][j]

放物品i,dp[i][j]=dp[i-1][j-w[i]]+v[i]

递推公式为:

 dp[i][j]=dp[i-1][j];//不放

 if(w[i]<=j)dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+v[i]);

  //当前物品体积小于j才可以放

两层for循环,先遍历物品或者先变量背包容量都可以

(2)第二种情况:一维dp[j]数组...相当于上一层数组拷贝下来,也叫滚动数组

dp[j]表示容量为j的背包所能放的最大价值

不放物品i,dp[j]=dp[j]

放物品i,dp[j]=dp[j-w[i]]+v[i]

递推公式为:

dp[j]=max(dp[j],dp[j-w[i]]+v[i])

双重for循环,必须先遍历物品,再遍历背包,同时背包要倒序遍历。

for(int i=0;i<n;i++){

        for(int j=t;j>=nums[i];j--){

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

        }

    }

原因:保证每个物品只添加一次

416 分割等和子集

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

1 <= nums.length <= 200

1 <= nums[i] <= 100

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
  • 背包的体积为sum / 2
  • 背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
  • 背包如何正好装满,说明找到了总和为 sum / 2 的子集。
  • 背包中每一个元素是不可重复放入。

0-1背包问题,相当于物品体积和价值都是nums[i]

对数组求和,sum为奇数一定不满足

求出容量为sum/2时能放入的最大价值,如果刚好放慢,则说明可以分割为等和子集。

根据数组长度和大小,可以确定dp数组的大小,100*200/2=10000

int max(int a,int b){return a>b?a:b;
}
/*0-1背包问题,相当于物品体积和价值都是nums[i]
对数组求和,sum为奇数一定不满足
求出容量为sum/2时能放入的最大价值,如果刚好放慢,则说明可以分割为等和子集
*/
bool canPartition(int* nums, int numsSize) {int sum=0,i,j;int dp[10005]={};//dp[j]表示背包体积为j能放入的最大体积for(i=0;i<numsSize;i++){sum+=nums[i];}if(sum%2!=0)return false;//数组和必为偶数else{for(i=0;i<numsSize;i++){//必须先遍历物品for(j=sum/2;j>=nums[i];j--){//倒序遍历,保证每个元素只添加一次dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);//放入物品i,dp[j-nums[i]]+nums[i}}}if(dp[sum/2]==sum/2)return true;else return false;
}

1049 最后一块石头的重量

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

示例 1:

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

示例 2:

输入:stones = [31,26,33,21,40]
输出:5

尽量把石头分为重量相近的两堆,保证相撞后重量最小

所以要尽可能分成重量为 sum / 2 的石头堆,这样剩下的石头堆也是 尽可能接近 sum/2 的重量

转化为背包问题,容量为sum/2时能装入的最大重量

剩下重量为sum-dp[sum/2],sum/2向下取整,所以剩下的重量一定大于dp[sum/2]

最后返回剩下重量与dp[sum/2]的差即可

int max(int a,int b){return a>b?a:b;
}
/*
尽量把石头分为重量相近的两堆,保证相撞后重量最小
所以要尽可能分成重量为 sum / 2 的石头堆,这样剩下的石头堆也是 尽可能接近 sum/2 的重量
转化为背包问题,容量为sum/2时能装入的最大重量
剩下重量为sum-dp[sum/2],sum/2向下取整,所以剩下的重量一定大于dp[sum/2]
最后返回剩下重量与dp[sum/2]的差即可
*/
int lastStoneWeightII(int* stones, int stonesSize) {int dp[15005]={};//dp[j]表示背包容量为j时所能放的最大价值int sum=0;for(int i=0;i<stonesSize;i++){sum+=stones[i];}int t=sum/2;for(int i=0;i<stonesSize;i++){for(int j=t;j>=stones[i];j--){dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);}}//sum向下取整,所以sum-dp[t]一定大于dp[t]return sum-dp[t]-dp[t];
}

总结:01背包问题,物品数量只有一个时,分为选和不选两种情况讨论

做题步骤

1、确定dp数组含义

2、确定递推公式

3、初始化

4、遍历。注意,二维dp数组遍历无顺序要求,一维dp数组必须先遍历物品,再遍历背包,并且背包要倒序遍历。循环内j>w[i]

相关文章:

  • 智能安全用电系统预防电气线路老化、线路或设备绝缘故障
  • 开箱即用:一款带世界时钟简约好用在线时间戳转换工具源码
  • 【PCB绘制】信号完整性准则
  • 【PCIE730】基于PCIe总线架构的4路10G光纤通道适配器
  • SpringCloud和SpringCloudAlibaba技术栈全面对比
  • 基于STC89C52RC和8X8点阵屏、独立按键的小游戏《打砖块》
  • 海外服务器安装Ubuntu 22.04图形界面并配置VNC远程访问指南
  • 【工具使用-数据库】MySQL数据库创建和使用
  • 反射内存网技术应用于数控系统
  • GAIA-2:用于自动驾驶的可控多视图生成世界模型
  • 使用stream进行列表循环和直接forEach循环的差异及使用场景
  • 支持远程搜索、分页加载和多选功能的 el-select 组件
  • SpringBoot自定义验证器:企业级参数校验架构设计与实践
  • 流量抓取工具(wireshark)
  • docker 中将dpo后的模型转为ollama能够调用的gguf格式
  • 对程序中使用到的字体进行子集化处理
  • C语言高频面试题——常量指针与指针常量区别
  • 文档处理控件Aspose.Words 教程:在 Word 中删除空白页完整指南
  • 【C++游戏引擎开发】第19篇:Compute Shader实现Tile划分
  • Cursor可视化大屏搭建__0420
  • 支持医企协同创新研究,上海已设立一系列产学研医融合项目
  • 京东:自21日起,所有超时20分钟以上的外卖订单全部免单
  • 从黄仁勋到美国消费者,都在“突围”
  • 希音、Temu告知美国消费者4月25日起涨价:关税变化导致运营成本上升
  • A股三大股指涨跌互现:房地产板块大幅上涨,两市成交9995亿元
  • 姜宏出任康复大学分管日常工作的副校长,明确为正厅级