力扣-最后一块石头的重量

186 阅读2分钟

「这是我参与2022首次更文挑战的第39天,活动详情查看:2022首次更文挑战」。

最后一块石头的重量

题目描述

有一堆石头,用整数数组stones表示。其中stones[i]表示第i块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别位xy,且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

示例3:

输入: stones = [1,2]
输出: 1

算法解析

这道题目实质上就是01背包问题的一个变换,这里的stones[i]就是weight[i],同时也是value[j]。 关于01背包问题,我们可以参照01背包问题的递推公式然后推出本题的递推公式。

image.png
dp[i][j]表示从下标为0到j的物品里任意取,放进容量为j的背包,价值总和最大是多少。
weight[i]表示第i个物品的重量,value[i]表示第i个物品的价值

由此我们可以推出本题的递推公式为

image.png 这里的背包容量是所有石头的重量之和sum/2,在这里我们以示例1为例,画出二维数组
stones[i]=[2,7,4,1,8,1],target=(2+7+4+1+8+1)/2=11

由递推公式得

dp[0][0]=0,dp[1][0]=0,dp[2][0]=0,dp[3][0]=0,dp[4][0]=0,dp[5][0]=0
dp[0][1]=0,
dp[1][1]=dp[0][1]=0,
dp[2][1]=dp[1][1]=0,
dp[3][1]=max{dp[2][1],dp[2][1-1]+1}=max(0,1)=1,
dp[4][1]=dp[3][1]=1,
dp[5][1]=max{dp[4][1],dp[4][1-1]+1}=max(1,1)=1,
dp[0][2]=max{dp[-1][2],dp[-1][2-1]+2}=max(0,2)=2,
dp[1][2]=dp[0][2]=2,
dp[2][2]=dp[1][2]=2,
dp[3][2]=max{dp[2][2],dp[2][2-1]+1}=max{2,1}=2,
dp[4][2]=dp[3][2]=2,
dp[5][2]=max{dp[4][2],dp[4][2-1]+1}=max{2,2}=2.
dp[0][3]=max{dp[-1][3],dp[-1][3-2]+2}=max(0,2)=2,
dp[1][3]=dp[0][3]=2,
dp[2][3]=dp[1][3]=2,
dp[3][3]=max{dp[2][3],dp[2][2]+1}=max{2,3}=3,
dp[4][3]=dp[3][3]=3,
dp[5][3]=max{dp[4][3],dp[4][2]+1}=max{3,3}=3,
dp[0][4]=max{dp[-1][4],dp[-1][2]+2}=max{0,2}=2,
dp[1][4]=dp[0][4]=2,
dp[2][4]=max{dp[1][4],dp[1][0]+4}=max{2,4}=4,
dp[3][4]=max{dp[2][4],dp[2][3]+1}={4,3}=4,
dp[4][4]=dp[3][4]=4,
dp[5][4]=max{dp[4][4],dp[4][3]+1}=max{4,4}=4

因为递推过多这里就不多加赘述了,计算好之后绘制表格如下

1234567891011
100222222222
200222227799
300224467799
4012345678910
5012345678910
6012345678910

代码解析

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum=0;
        for(int i=0;i<stones.length;i++){
            sum+=stones[i];
        }
        //求出target
        int target=sum/2;
        //定义dp数组
        int[][] dp=new int[stones.length][target+1];
        for(int j=stones[0];j<=target;j++){
            dp[0][j]=stones[0];
        }
        //递推公式
        for(int i=1;i<stones.length;i++){
            for(int j=1;j<=target;j++){
                if(j>=stones[i]){
                    dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i]);
                }else{
                    dp[i][j]=dp[i-1][j];
                }
            }
        }
        return (sum-dp[stones.length-1][target])-dp[stones.length-1][target];
    }
}