动态规划LeetCode-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
提示:

1 <= stones.length <= 30

1 <= stones[i] <= 100

写这题之前可以先去写一下LeetCode的416.分割等和子集,代码和思路基本一致,只是如何把这题1049. 最后一块石头的重量Ⅱ如何转换为分割等和子集

我们看一下题目并想一想,本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了

且和416.分割等和子集非常相似,可以先去写416再来写本题,416.分割等和子集思路可以看我的这篇文章动态规划LeetCode-416.分割等和子集-CSDN博客

01背包问题

背包问题,大家都知道,有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

01背包一维滚动数组递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

这题不过多讲解,因为思路和416.分割等和子集差不多,主要是最后返回值的那里,分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]。因为我们在计算target = sum/2的时候是向下取整的,所以sum - dp[target] >= dp[target];

那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]。

动规五部曲(dp含义、递推公式、初始化、遍历顺序、打印数组)

dp含义:dp[j]表示容量为j的背包,所背的物品价值最大可以为dp[j]。

递推公式:本题中每一个元素的数值既是重量,也是价值。所以

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

初始化:背包容量为j=0,物品最大价值为dp[0]=0这个好理解,那其他下标初始化也为0是为什么呢,因为dp数组在递推的过程中取得最大的价值,把下标初始成负无穷小,就不会被初始值覆盖,这里初始为0即可,也是一样的。

递推公式:

这里是用一维滚动数组来解决,所以物品遍历的for循环放在外层,遍历背包的for循环放在内层,然后题目说物品i只能放一次,所以且内层for循环倒序遍历!

因为倒序遍历是为了保证物品i只被放入一次!。但如果一旦正序遍历了,那么物品0就会被重复加入多次!

打印数组:当遇到疑惑或者提交错误时,打印数组出来比较快速的看看哪一步有错。

以下是我在力扣c语言提交的代码,仅供参考:

一维滚动数组:

复制代码
int lastStoneWeightII(int* stones, int stonesSize) {  
    //给出容量和数值大小范围,求的还是一半,所以数组大小为30*100/2+1
  int dp[1501]={0};
  int sum = 0;int target = 0;

  //初始化
  memset(dp,0,sizeof(dp));
  dp[0] = 0;
  
  for(int i = 0;i<stonesSize;i++)
  {
    sum += stones[i];
  }
  target = sum / 2;

  //先遍历物品
  for(int i = 0;i<stonesSize;i++)
  {
    //再遍历背包,且是倒序遍历,保证物品i只被放入一次!
    for(int j = target;j>=stones[i];j--)
    {
        //01背包递推公式dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        //本题中每一个元素的数值既是重量,也是价值
        dp[j] = dp[j] > dp[j-stones[i]]+stones[i] ? dp[j] : dp[j-stones[i]]+stones[i];
    }
  }

  //分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]。
  //因为我们在计算target = sum/2的时候是向下取整的,所以sum - dp[target] >= dp[target];
  //那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]。
  return sum - dp[target] - dp[target];
}  

这里也给出用二维数组求解的代码:

复制代码
int lastStoneWeightII(int* stones, int stonesSize) {  
    int sum = 0;
    for(int i = 0;i<stonesSize;i++)
    {
        sum += stones[i];
    }
    int target = sum / 2;
    int dp[stonesSize+1][target+1];
    memset(dp,0,sizeof(dp));
    for(int i = stones[0];i<=target ;i++)
    {
        dp[0][i] = stones[0];
    }
    for(int i = 1;i<stonesSize;i++)
    {
        for(int j = 0;j<=target;j++)
        {
            if(j < stones[i])
            {
                dp[i][j] = dp[i-1][j];
            }
            else
            {
                dp[i][j] = dp[i-1][j] > (dp[i-1][j-stones[i]]+stones[i]) ? dp[i-1][j]:(dp[i-1][j-stones[i]]+stones[i]);
            }
        }
    }

    return (sum-dp[stonesSize-1][target]) - dp[stonesSize-1][target];
相关推荐
爱装代码的小瓶子2 小时前
数据结构之队列(C语言)
c语言·开发语言·数据结构
爱喝矿泉水的猛男3 小时前
非定长滑动窗口(持续更新)
算法·leetcode·职场和发展
YuTaoShao3 小时前
【LeetCode 热题 100】131. 分割回文串——回溯
java·算法·leetcode·深度优先
YouQian7723 小时前
Traffic Lights set的使用
算法
快乐飒男4 小时前
哈希表(c语言)
c语言·哈希算法·散列表
go54631584655 小时前
基于深度学习的食管癌右喉返神经旁淋巴结预测系统研究
图像处理·人工智能·深度学习·神经网络·算法
aramae5 小时前
大话数据结构之<队列>
c语言·开发语言·数据结构·算法
大锦终5 小时前
【算法】前缀和经典例题
算法·leetcode
想变成树袋熊5 小时前
【自用】NLP算法面经(6)
人工智能·算法·自然语言处理
cccc来财6 小时前
Java实现大根堆与小根堆详解
数据结构·算法·leetcode