Day 43 1049. 最后一块石头的重量 II 494. 目标和 474.一和零

最后一块石头重量Ⅱ

有一堆石头,每块石头的重量都是正整数。

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

如果 x == y,那么两块石头都会被完全粉碎;

如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。

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

示例:

  • 输入:[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],这就是最优值。

提示:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 1000

​ 关键点在想到:让石头尽量分成重量相同的两堆,相撞之后剩下的石头最小

​ 又因为唯一性,很自然就化解为了01背包问题;

​ 本题物品的重量为stones[i],物品的价值也为stones[i]。

​ 对应着01背包里的物品重量weight[i]和 物品价值value[i]。

​ 动规五部曲:
​ 1.dp数组含义:
​ dp[j]表示容量为j的背包,最多可以背的最大重量为dp[j];

​ 2.确定递推公式:

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

​ 3.初始化:

​ 因为提示中给出1 <= stones.length <= 30,1 <= stones[i] <= 1000,所以最大重量就是30 * 1000 。

​ 而我们要求的target其实只是最大重量的一半,所以dp数组开到15000大小就可以了。

​ 当然也可以把石头遍历一遍,计算出石头总重量 然后除2,得到dp数组的大小。

​ 我这里就直接用15000了。

​ 即vector<int> dp (15001, 0);

​ 4.遍历顺序:

​ 和之前的一致,内层倒序:

for (int i = 0; i < stones.size(); i++) { // 遍历物品for (int j = target; j >= stones[i]; j--) { // 遍历背包dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);}
}

​ 5.打印数组:

#include<iostream>
#include<vector>
#include<numeric>
using namespace std;
class solution {
public:int lastStoneWeightII(vector<int>& stones){int sum = accumulate(stones.begin(), stones.end(), 0);//向下取整求背包的最大空间int target = sum / 2;vector<int> dp(15001, 0);//根据测试案例调整dp数组大小for (int i = 0; i < stones.size(); i++) {cout << "The current dp array is:" << endl;for (int j = target; j >= stones[i]; j--) {dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);}for (int k = 0; k <= target; k++) {cout << dp[k] << ' ';}cout << endl;}return sum - 2 * dp[target];}
};
int main() {solution mySolution;vector<int> tests = { 2,4,1,1 };cout << "The result is:" << mySolution.lastStoneWeightII(tests) << endl;return 0;
}

目标和

给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例:

  • 输入:nums: [1, 1, 1, 1, 1], S: 3
  • 输出:5

解释:

  • -1+1+1+1+1 = 3
  • +1-1+1+1+1 = 3
  • +1+1-1+1+1 = 3
  • +1+1+1-1+1 = 3
  • +1+1+1+1-1 = 3

一共有5种方法让最终目标和为3。

提示:

  • 数组非空,且长度不会超过 20 。
  • 初始的数组的和不会超过 1000 。
  • 保证返回的最终结果能被 32 位整数存下。

​ 先不考虑是否为背包问题,先分析问题本身;其实还是可以视为一个子集划分的问题;

​ 由于数组非负,所以target = sum_plus - sum_minus其中,sum_plus + sum_minus = sum;

​ 所以sum_minus = (sum - target)/2;

​ 找这个sum_minus是否存在即可

​ 动规五部曲:

​ 1.dp数组及其下标含义:

​ dp[j]表示sum_minus为j时有dp[j]种方法使得子集和为sum_minus;

​ 2.确认递推公式:

​ 这里借用二维数组来方便理解:

​ dp[i][j] = dp[i-1][j] + dp[i-1][j -nums[i]];

​ dp[i - 1][j]是不将物品i放入背包的方式数,dp[i-1][j - nums[i]]使将物品i放入背包的方式数;

​ 再次确定此时一维dp数组含义,dp[j]指的是子集和为j的情况下的路径数:

​ 要用若干个元素组成和为j的方案数,那么有两种选择:不选第i个元素或者选第i个元素;

​ 如果不选第i个元素,那么原来已经有多少种方案数就不变;

​ 如果选第i个元素,那么就是在已有的基础上,加上剩下要组成和为j - nums[i] 的方案数就等于dp[j - nums[i]];

​ 所以,很显然,dp[j] = dp[j] + dp[j - nums[i]];

​ 3.初始化:

​ 很显然,根据上面的推导,dp[0] = 1,不然的话所有项都是0,其余dp[j] = 0;

​ 4.遍历顺序:

​ 经典一维01背包

​ 5.打印数组:
image-20240510200606342

image-20240510203829314

#include<numeric>
#include<iostream>
#include<vector>using namespace std;class solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum = accumulate(nums.begin(), nums.end(), 0);if (abs(target) > sum)	return 0;if ((sum -  target) % 2 == 1)	return 0;int sum_minus = (sum - target) / 2;vector<int> dp(1001, 0);//因为初始数组和小于1000,target可取范围为-1000-1000,所以sum_minus <= 1000恒成立dp[0] = 1;for (int i = 0; i < nums.size(); i++) {for (int j = sum_minus; j >= nums[i]; j--) {dp[j] += dp[j - nums[i]];}cout << "The current dp array will be shown as below:" << endl;for (int k = 0; k <= sum_minus; k++) {cout << dp[k] << ' ';}cout << endl;}return dp[sum_minus];}
};int main() {cout << "Please enter the test sequences(requiring non-negative integers) end with -1:" << endl;vector<int> tests;int num;while (cin >> num && num != -1) {tests.push_back(num); // 将输入的整数添加到vector中  }cout << "Please enter the target sum:" << endl;int target;cin >> target;solution mySolution;cout << "There are " << mySolution.findTargetSumWays(tests, target) << " ways to get to the target sum." << endl;return 0;
}

一和零

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

示例 1:

  • 输入:strs = [“10”, “0001”, “111001”, “1”, “0”], m = 5, n = 3
  • 输出:4
  • 解释:最多有 5 个 0 和 3 个 1 的最大子集是 {“10”,“0001”,“1”,“0”} ,因此答案是 4 。 其他满足题意但较小的子集包括 {“0001”,“1”} 和 {“10”,“1”,“0”} 。{“111001”} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。

示例 2:

  • 输入:strs = [“10”, “0”, “1”], m = 1, n = 1
  • 输出:2
  • 解释:最大的子集是 {“0”, “1”} ,所以答案是 2 。

提示:

  • 1 <= strs.length <= 600
  • 1 <= strs[i].length <= 100
  • strs[i] 仅由 ‘0’ 和 ‘1’ 组成
  • 1 <= m, n <= 100

​ 看起来好像很复杂,实际上就是拥有两个维度的背包,容量分别为m和n;

​ 至于strs虽然看上去有许多重复的0 1,但是实际上每个strs[i]只能取一次,所以还是0-1背包问题;

​ 动规五部曲:
​ 1.dp数组含义及其下标:

​ dp[i][j],表示在子集在最多有m个0,n个1的前提下,最多可以选择有dp[i][j]个元素;

​ 2.递推公式:

​ 先考虑最原始的0-1背包问题:

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

​ 对于本题:

​ dp[i][j] = max(dp[i][j], dp[i - zero_nums][y - one_nums] +1);

​ 3.初始化:
​ 很显然,此时的dp[0][0] = 0;

​ 又因为递推公式是需要比较最大值的,所以所有dp[i][j] = 0;

​ 4.遍历顺序:

​ 经典倒序:

		for (string str : strs) { // 遍历物品int oneNum = 0, zeroNum = 0;for (char c : str) {if (c == '0') zeroNum++;else oneNum++;}for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!for (int j = n; j >= oneNum; j--) {dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);}}}

​ 5.打印dp数组:
在这里插入图片描述

#include<iostream>
#include<string>
#include<vector>
using namespace std;
class solution {
public:int findMaxFormvector(vector<string>& strs, int m, int n) {vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));//dp数组初始化for (vector<string>::size_type i = 0; i < strs.size(); i++) {string str = strs[i];int zero_nums = 0, one_nums = 0;for (string::size_type j = 0; j < str.size(); j++) {char c = str[j];if (c == '1')	one_nums++;else zero_nums++;}for (int i = m; i >= zero_nums; i--) {for (int j = n; j >= one_nums; j--) {dp[i][j] = max(dp[i][j], dp[i - zero_nums][j - one_nums] + 1);}}cout << "The current dp array will be shown as below:" << endl;for (int i = 0; i <= m; i++) {for (int j = 0; j <= n; j++) {cout << dp[i][j] << ' ';}cout << endl;}}return dp[m][n];}
};int main() {cout << "Please enter the strings(only 0 and 1 are allowed),seperated by enter,quit with\"stop\"" << endl;solution ms;vector<string> strs;string input;while (true) {getline(cin, input);if (input == "stop")	break;strs.push_back(input);}cout << "Please enter the maximum '0' numbers and '1' numbers:" << endl;int m, n;cin >> m >> n;cout << "The maximum subset's elements is: " << ms.findMaxFormvector(strs, m, n) << endl;return 0;
}

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

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

相关文章

提取网页元数据的Python库之lassie使用详解

概要 Lassie是一个用于提取网页元数据的Python库,它能够智能地抓取网页的标题、描述、关键图像等内容。Lassie的设计目的是为了简化从各种类型的网页中提取关键信息的过程,适用于需要预览链接内容的应用场景。 安装 安装Lassie非常简单,可以通过Python的包管理器pip进行安…

芋道系统springcloud模块启动报错,枚举类不能为空

问题描述&#xff1a; Error starting ApplicationContext. To display the conditions report re-run your application with debug enabled. 2024-05-10 15:50:15.756 | ERROR 9120 | main [TID: N/A] o.s.b.d.LoggingFailureAnalysisReporter | ************************…

TriCore: Architecture

说明 本文是 英飞凌 架构文档 TriCore TC162P core archiecture Volume 1 of 2 (infineon.com) 的笔记&#xff0c;稍作整理方便查阅&#xff0c;错误之处&#xff0c;还请指正&#xff0c;谢谢 :) 1. Architecture 2. General Purpose & System Register 名词列表&#…

通俗的理解网关的概念的用途(三):你的数据包是如何到达下一层的

其实&#xff0c;这一章我写不好&#xff0c;因为这其中会涉及到一些计算和一些广播等概念&#xff0c;本人不善于此项。在此略述&#xff0c;可以参考。 每台设备的不同连接在获得有效的IP地址后&#xff0c;会根据IP地址的规则和掩码的规则&#xff0c;在操作系统和交换机&a…

【前端基础】CSS样式+Vue中绘制时间轴

深度选择器 在 Vue.js 中&#xff0c;/deep/、>>>、:deep 和 ::v-deep 这些都是深度选择器&#xff0c;用于修改子组件的样式。它们主要用于解决作用域样式和组件样式之间的冲突问题。 1. /deep/ 或 >>> /deep/ 和 >>> 是相同的选择器&#xff0c;…

uniApp开发小程序自定义tabBar底部导航栏+tabBar中间凸起自定义样式实现

先看效果是否可以满足你们&#xff0c;如果可以满足你只要一步一步照着做绝对没有错。 本人技术不佳&#xff0c;研究了一整天&#xff0c;全网的大佬们写的博客看的晕头转向&#xff0c;避免大伙再走弯路&#xff0c;跟着我以下步骤一点一点来绝对可以实现。 最终效果图&#…

二、ArkTS语法学习

上一篇我们学习了Harmony​​​​​​开发入门&#xff0c;接下来我们简单学习一下ArkTS 一、ArkTS起源 在TS基础上扩展了申明式UI和状态管理等相应的能力 二、TS语法基础 1、类型 any 任意类型&#xff08;动态类型&#xff09; let a: any 5; a "str" num…

system函数和popen函数

system函数 #include <stdlib.h> *int system(const char command); system函数在linux中的源代码&#xff1a; int system(const char * cmdstring) {pid_t pid;int status;if(cmdstring NULL){return (1);}if((pid fork())<0){status -1;}else if(pid 0){ //子…

Linux:进程通信(三)信号的捕捉

目录 一、信号捕捉函数 1、signal函数 2、sigaction函数 二、用户态与内核态 1、用户态 2、内核态 用户态与内核态转换 三、volatile关键字 四、SIGCHLD信号 一、信号捕捉函数 1、signal函数 signal函数是C语言标准库中的一个函数&#xff0c;用于处理Unix/Linux系…

【spring】Security 密码加密算法

Spring Security 提供了多种密码加密算法&#xff0c;用于在存储用户密码时进行加密&#xff0c;以增强安全性。 查看org.springframework.security.crypto.factory.PasswordEncoderFactories 以下是一些常用的密码加密算法&#xff1a; BCryptPasswordEncoder&#xff1a; 这…

韩顺平0基础学Java——第8天

p155-168 数组&#xff08;第六章&#xff09; 数组可以存放多个同一类型的数据&#xff0c;数组也是一种数据类型&#xff08;引用类型&#xff09;。 即&#xff0c;数组就是一组数据~ 例&#xff1a;double [] hens {1,2,3,4,5,6}; 新建了一组鸡&#xff0c;里面有6个。…

Java医院绩效考核系统源码maven+Visual Studio Code一体化人力资源saas平台系统源码

Java医院绩效考核系统源码mavenVisual Studio Code一体化人力资源saas平台系统源码 医院绩效解决方案包括医院绩效管理&#xff08;BSC&#xff09;、综合奖金核算&#xff08;RBRVS&#xff09;&#xff0c;涵盖从绩效方案的咨询与定制、数据采集、绩效考核及反馈、绩效奖金核…