Trace
6.8 每日一题
传送门:https://leetcode-cn.com/problems/last-stone-weight-ii/
Problem
有一堆石头,用整数数组 stones
表示。其中 stones[i]
表示第 i
块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x
和 y
,且 x <= y
。那么粉碎的可能结果如下:
- 如果
x == y
,那么两块石头都会被完全粉碎;
- 如果
x != y
,那么重量为 x
的石头将会完全粉碎,而重量为 y
的石头新重量为 y-x
。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0
。
示例
1 2 3 4 5 6 7
| 输入: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],这就是最优值。
|
1 2
| 输入:stones = [31,26,33,21,40] 输出:5
|
数据范围
1 2
| 1 <= stones.length <= 30 1 <= stones[i] <= 100
|
思路
思考对于粉碎的两个石头,粉碎后的石头重量为$stones[i]-stones[j]$,用这个石头与$stones[k]$来粉碎,假设$stones[k]>(stones[i]-stones[j])$,粉碎后的结果为$stones[k]-stones[i]+stones[j]$。
粉碎的本质就是给$stones$数组中每个数前面加+
和-
。
题意转化为给$stones$数组中每个数前面加+
和-
,使得最后结果的绝对值最小。
记$stones$数组的和为$sum$,前面加-
的数字的和为$neg$,$ans=sum-2*neg$,要使$ans$绝对值小,即$neg$取小于等于$sum/2$的最大的值。
题意转化为从$stones$数组中找若干个数,使得若干个数的和(代价,类似01背包中的体积)在满足不超过$sum/2$的条件下最大。
到这就是裸的01背包。
有几种dp的写法:
第一种
状态表示:
dp[i][j]
集合:从前$i$个数中取,代价(和)不超过$j$的取法。
属性:和的最大值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution { public: int dp[30001]; int lastStoneWeightII(vector<int>& stones) { int sum=0; for(int i=0;i<stones.size();i++){ sum+=stones[i]; } int ans=sum/2; for(int i=1;i<=stones.size();i++){ for(int j=ans;j>=stones[i-1];j--){ dp[j]=max(dp[j],dp[j-stones[i-1]]+stones[i-1]); } } return sum-2*dp[ans]; } };
|
第二种
状态表示:
dp[i][j]
集合:从前$i$个数中取,得到总和为$j$的取法。
属性:数量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class Solution { public: int lastStoneWeightII(vector<int>& stones) { int sum=0; for(int i=0;i<stones.size();i++){ sum+=stones[i]; } int ans=sum/2; int f[31][1501]; f[0][0]=1; for(int i=1;i<=stones.size();i++){ for(int j=0;j<=ans;j++){ f[i][j]=f[i-1][j]; if(j>=stones[i-1]){ f[i][j]=f[i][j]+f[i-1][j-stones[i-1]]; } } } int neg=0; for(int i=ans;i>=0;i--){ if(f[stones.size()][i]>0){ neg=i; break; } } return sum-2*neg; } };
|
第三种
状态表示:
dp[i][j]
集合:从前$i$个数中取,得到总和为$j$的取法。
属性:0或1代表是否能够有这种取法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Solution { public: int f[30001]; int lastStoneWeightII(vector<int>& stones) { int sum=0; for(int i=0;i<stones.size();i++){ sum+=stones[i]; } cout<<sum<<endl; int tmp=sum/2; f[0]=1; for(int i=1;i<=stones.size();i++){ for(int j=tmp;j>=stones[i-1];j--){ f[j]=max(f[j],f[j-stones[i-1]]); } } for(int i=tmp;i>=0;i--){ if(f[i]!=0){ cout<<i<<endl; return sum-2*i; } } return 0; } };
|