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

常州网站推广培训江宁滨江网站建设

常州网站推广培训,江宁滨江网站建设,高端定制网站建设,城市建设网站金1049. 最后一块石头的重量 II 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果…

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

有一堆石头,用整数数组 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

提示:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 100
class Solution {
public:int lastStoneWeightII(vector<int>& stones) {vector<int> dp(150001,0);int sum = 0;for(int i=0;i<stones.size();i++){sum+=stones[i];}int target = sum/2;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]);}}return sum-dp[target]-dp[target];}
};

解题思路:

问题背景

问题描述:有一堆石头,每次从中挑选两块石头碰撞,碰撞后会得到一块新石头(重量为两块石头的差值),直到只剩下一块石头或没有石头。求最后可能剩下的最小石头重量。

核心思路转化

这个问题可以转化为:

  • 将石头分成两堆,使两堆的重量尽可能接近
  • 两堆重量的差值就是最后可能剩下的最小重量

这是因为每次碰撞相当于从总重量中减去 twice of 较小的那块石头的重量,最优策略是让两堆石头重量尽可能接近。

代码原理详解

  1. 动态规划数组定义
    dp[j]表示能从石头中选出若干块,使得它们的总重量最大为j

  2. 计算目标值
    sum是所有石头的总重量
    targetsum/2,我们尝试找到不超过这个值的最大子集和

  3. 0-1 背包问题求解

    • 外层循环遍历每块石头(物品)
    • 内层循环从target往当前石头重量遍历(0-1 背包的典型做法,避免重复使用同一物品)
    • dp[j] = max(dp[j], dp[j - stones[i]] + stones[i])表示对于重量j,要么不选当前石头保持dp[j],要么选当前石头得到dp[j - stones[i]] + stones[i]
  4. 计算结果
    sum - dp[target] - dp[target]表示总重量减去两倍的最大子集和(即两堆石头的重量差)

这个解法的时间复杂度是 O (n×target),空间复杂度是 O (target),其中 n 是石头数量,target 是总重量的一半。

494. 目标和

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum = 0;for (int i = 0; i < nums.size(); i++) sum += nums[i];if (abs(target) > sum) return 0; // 此时没有方案if ((target + sum) % 2 == 1) return 0; // 此时没有方案int bagSize = (target + sum) / 2;vector<int> dp(bagSize + 1, 0);dp[0] = 1;for(int i=0;i<nums.size();i++){for(int j = bagSize;j>=nums[i];j--){dp[j] += dp[j-nums[i]];}}return dp[bagSize];}
};

给每个数字添加+-,本质上是将数组分成两组:

  • 一组数字前面加+(和为left
  • 另一组数字前面加-(和为right

根据题意,我们有:

plaintext

left - right = target  // 表达式结果等于target
left + right = sum     // 所有数字的总和

将两式相加可得:2*left = target + sum,即 left = (target + sum) / 2

因此问题转化为:找到有多少种方法可以从数组中选出一些数字,使它们的和等于 left

代码解析

  1. 边界条件判断

    if (abs(target) > sum) return 0; // 目标值的绝对值大于总和,不可能实现
    if ((target + sum) % 2 == 1) return 0; // 和为奇数,无法被2整除,没有方案
    
  2. 计算背包大小

    int bagSize = (target + sum) / 2;
    
     

    这里的bagSize就是我们要找的left值,即需要选出和为bagSize的数字组合

  3. 动态规划初始化

     
    vector<int> dp(bagSize + 1, 0);
    dp[0] = 1; // 表示凑出和为0的方法有1种(什么都不选)
    
     

    dp[j]的含义是:有多少种方法可以选出和为 j 的数字组合

  4. 填充 DP 数组

     
    for (int i = 0; i < nums.size(); i++) { // 遍历每个数字for (int j = bagSize; j >= nums[i]; j--) { // 倒序遍历背包dp[j] += dp[j - nums[i]];}
    }
    
     
    • 对于每个数字,我们可以选择 "放入背包"(即该数字前面加+)或 "不放入背包"(即该数字前面加-
    • 状态转移方程dp[j] += dp[j - nums[i]]表示:凑出和为 j 的方法数 = 不选当前数字的方法数 + 选当前数字的方法数(此时需要加上凑出 j-nums [i] 的方法数)
    • 倒序遍历是为了保证每个数字只被使用一次(0-1 背包特性)
  5. 返回结果

    return dp[bagSize];
    
     

    dp[bagSize]就是能凑出和为bagSize的方法总数,也就是我们要求的答案

示例说明

nums = [1,1,1,1,1]target = 3为例:

  • 总和sum = 5
  • bagSize = (3 + 5) / 2 = 4
  • 问题转化为:有多少种方法从数组中选出和为 4 的数字
  • 答案是 5 种,对应 5 种不同的表达式

474. 一和零

class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {vector<vector<int>> result(m+1,vector<int> (n+1,0));for(string str:strs){int onenumber = 0,zeronumber = 0;for (char c : str) {if (c == '0') zeronumber++;else onenumber++;}for(int i=m;i>=zeronumber;i--){for(int j=n;j>=onenumber;j--){result[i][j] = max(result[i][j],result[i-zeronumber][j-onenumber]+1);}}}return result[m][n];}};

问题理解

给定一组二进制字符串,每个字符串包含0和1。我们需要选择一些字符串,使得:

  • 所有选中字符串中0的总数不超过 m

  • 所有选中字符串中1的总数不超过 n

  • 选中的字符串数量尽可能多

动态规划思路

1. 状态定义

dp[i][j] 表示:使用最多 i 个0和 j 个1时,能够组成的最大字符串数量

2. 状态转移

对于每个字符串,我们有两种选择:

  • 不选这个字符串dp[i][j] 保持不变

  • 选这个字符串dp[i][j] = dp[i - zeros][j - ones] + 1

状态转移方程:

cpp

dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1);

3. 为什么从后往前遍历?

这是0-1背包问题的核心技巧:

  • 从后往前遍历:确保每个字符串只被使用一次(避免重复选择)

  • 如果从前往后遍历,会变成完全背包问题(每个物品可以选多次)

4. 具体步骤

  1. 初始化:创建 (m+1) × (n+1) 的二维数组,初始值为0

  2. 遍历每个字符串

    • 计算当前字符串的0和1的数量

    • 从 m 到 zeros,从 n 到 ones 反向更新dp表

  3. 返回结果dp[m][n] 就是最大数量

示例说明

假设 strs = ["10", "0001", "111001", "1", "0"]m = 5n = 3

对于字符串 "10":

  • zeros = 1, ones = 1

  • 更新所有 i >= 1 且 j >= 1 的位置

对于字符串 "0001":

  • zeros = 3, ones = 1

  • 更新所有 i >= 3 且 j >= 1 的位置

以此类推...

时间复杂度

  • 外层循环:O(S),S为字符串数量

  • 内层循环:O(M × N)

  • 总复杂度:O(S × M × N)

空间复杂度

  • O(M × N),即dp表的大小

这种解法巧妙地利用了动态规划和背包问题的思想,通过反向遍历确保每个字符串只被考虑一次,从而找到最优解。

http://www.dtcms.com/a/586293.html

相关文章:

  • 幻灯片网站源码临安网站设计
  • 自己做的游戏
  • 海南住房城乡建设网站淘宝导购网站模板
  • 三维空间平面方程
  • 网站 售后服务申请域名邮箱
  • 手机App里的动画是如何实现的-安卓动画深入探索
  • Lua中的可变参数
  • 夜晚的梦
  • 销售平台网站建设方案手机网站开发建设方案
  • Git 入门教程
  • 建行企业网站如何学编程入门教程
  • 做国外直播网站有哪些宜春做网站 黑酷seo
  • 重生归来,我要成功 Python 高手--day33 决策树
  • AI大模型全景图:十大核心能力与十大应用领域详解,附学习资源(建议收藏)
  • 学习Linux——软件管理
  • 广东品牌网站建设报价表网站建设中的财务预算
  • 什么网站的易用性重庆seo网站推广费用
  • 速卖通测评自养号技术:搭建安全稳定账号体系,流量销量双突破
  • 22-webpack案例:36kr
  • PCB批量处理命令
  • 网站建设 制作教程 pdf在山东和网页有关的公司
  • Go 语言接口
  • wordpress网站如何迁移电商网站建设课程
  • Keil编译出现:Missing Compiler Version 5
  • 十大免费网站推广平台有哪些创意设计素材
  • 为什么网站浏览不是做的那样农村自建房设计图软件
  • 如何设计一份精美到ppt
  • 网站如何做ssl认证计算机网站建设是什么
  • MySQL——数据库基础
  • 配置flutter鸿蒙的环境和创建并运行第一个flutter鸿蒙项目【精心制作】