1049.最后一块石头的重量II 文章:14. 最后一块石头的重量II
题目:1049. 最后一块石头的重量 II
【题目】
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头最小的可能重量。如果没有石头剩下,就返回 0。
【思路】
本题的题意:尽量将石头分成重量相等的两堆,返回这两堆的差值
1.确认数组及其下标含义
dp[i]:在背包的重量为i时,dp[i]为物品的最大价值(能背的最大重量)
2.确认递推公式
dp[j] = max(dp[j] , dp[j - stone[i]] + stone[i])
3.数组初始化
题目数值为正整数,非零下标初始化为0即可。
dp[0] = 0
4.确定遍历顺序
因为本题我们使用的是01背包 ,因此需要先遍历物品,然后再遍历背包容量且倒序!
5.举例推导
…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public int lastStoneWeightII (int [] stones) { int sum = 0 ; for (int i = 0 ; i < stones.length;i++){ sum += stones[i]; } int target = sum/2 ; int []dp = new int [target+1 ]; dp[0 ] = 0 ; for (int i = 0 ; i < stones.length ; i++ ){ for (int j = target ; j >= stones[i];j--){ dp[j] = Math.max(dp[j],dp[j - stones[i]] + stones[i]); } } return sum - dp[target]-dp[target]; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func lastStoneWeightII (stones []int ) int { sum := 0 for i := range stones{ sum += stones[i] } target := sum/2 dp := make ([]int ,target+1 ) for i:=0 ;i< len (stones);i++{ for j := target;j>= stones[i];j--{ dp[j] = max(dp[j],dp[j-stones[i]]+stones[i]) } } return sum-dp[target]-dp[target] }
494.目标和 文章:16. 目标和
题目:494. 目标和
【思路】
使用两个集合:加法的集合、减法的集合
加法的集合(left) - 减法的集合(right)= target
left + right = sum
right = sum-left
left - (sum-left) = target
2left - sum = target(sum、target是固定的)
因此 left = (sum+target)/2
本题转化为 =》 有多少个集合可以装满left容量的背包(01背包)
1.确认数组及其下标含义
dp[j]:容量为j,装满j容量的方法有dp[j]种
2.递推公式推导
dp[j] += dp[j - nums[i]]
3.数组初始化
由于本题的递推公式是累加(初始化为0的话整个数组都是0了) && 容量为0时,什么都不去也可以看作一种方法 ==》dp[0] = 1
题目提供的都是正整数,因此非零下标初始化为0即可
4.确认遍历顺序
01背包,先遍历物品,再遍历背包且倒序
5.举例推导
。。。
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 30 31 class Solution { public int findTargetSumWays (int [] nums, int target) { int sum = 0 ; for (int i = 0 ; i < nums.length;i++){ sum += nums[i]; } if (sum < target)return 0 ; if ( (sum+target) %2 == 1 )return 0 ; int bagsize = (sum-target)/2 ; int []dp = new int [bagsize+1 ]; dp[0 ] = 1 ; for (int i = 0 ; i < nums.length ;i++){ for (int j = bagsize ; j >= nums[i];j--){ dp[j] += dp[j-nums[i]]; } } return dp[bagsize]; } }
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 30 func findTargetSumWays (nums []int , target int ) int { sum := 0 for _,i := range nums{ sum += i } if abs(target) > sum{ return 0 } if abs(target) >sum{ return 0 } if (sum + target) %2 == 1 { return 0 } left:= (sum+target)/2 dp := make ([]int ,left+1 ) dp[0 ] =1 for i := 0 ;i < len (nums);i++{ for j := left;j >= nums[i];j--{ dp[j] += dp[j-nums[i]] } } return dp[left] } func abs (x int ) int { return int (math.Abs(float64 (x))) }
474.一和零 文章:17. 一和零
题目:474. 一和零
【思路】
本题的物品只能用一次 =》01背包
而m和n相当于是一个背包,两个维度的背包。
1.确定dp数组及其下标的含义
dp[i] [j]:最多有i个0和j个1的strs的最大子集的大小为dp[i] [j]
2.确定递推公式
dp[i] [j]可以由前一个strs里的字符串推导出来,strs的字符串里有zeroNum个0,oneNum个1
dp[i] [j]就可以是dp[i - zeroNum] [j - oneNum]+1
然后在遍历的过程中取dp[i] [j] 的最大值
字符串的zeroNum和oneNum相当于物品的重量weight[i],而字符串本身的个数就相当于物品的价值value[i]
=》递推公式:dp[i] [j] = max(dp[i] [j],dp[i - zeroNum] [j - oneNum]+1(物品有两个维度)
3.dp数组如何初始化
本题中物品的价值为正整数,因此非零下标的数值初始化为0即可
4.确定遍历顺序
01背包,先遍历物品,然后再遍历背包且倒序
5.举例推导
。。。
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 class Solution { public int findMaxForm (String[] strs, int m, int n) { int [][]dp = new int [m+1 ][n+1 ]; for (String str : strs){ int zeroNum = 0 ; int oneNum = 0 ; for (int i = 0 ; i < str.length();i++){ char c = str.charAt(i); if (c == '0' )zeroNum++; if (c == '1' )oneNum++; } for (int i = m ; i>= zeroNum;i--){ for (int j = n;j >= oneNum;j--){ dp[i][j] = Math.max(dp[i][j],dp[i-zeroNum][j-oneNum]+1 ); } } } return dp[m][n]; } }
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 func findMaxForm (strs []string , m int , n int ) int { dp := make ([][]int ,m+1 ) for i,_:=range dp{ dp[i] = make ([]int ,n+1 ) } for i:=0 ;i < len (strs);i++{ zeroNum ,oneNum := 0 ,0 for _, num := range strs[i]{ if num =='0' { zeroNum++ } } oneNum = len (strs[i]) - zeroNum for a1 := m;a1 >= zeroNum;a1--{ for a2 :=n;a2>=oneNum;a2--{ dp[a1][a2] = max(dp[a1][a2],dp[a1 - zeroNum][a2 -oneNum]+1 ) } } } return dp[m][n] } func maxa (a,b int ) int { if a > b{ return a } return b }
【算法总结】
最后一块石头的重量II:使用01背包,思路是将石头尽可能分成重量相等的两份,实践是将石头总重量的一半当作背包的最大容量,单个石头的重量作为物品的价值,循环遍历比较得到最大值。然后将总重量减去两份的背包最大值即可得出石头的重量差值。
目标和:使用01背包,思路是将目标和以及数组的加法集合、减法集合、数组总和凑出一条等式,通过求取加法集合(或减法集合)的最大价值获得凑到该最大价值有多少种方法 ,因此是采用累加的方式来得到最大价值。
一和零:使用01背包,思路是将0和1看作两个维度的背包,将str字符串数组看作物品。因为有两个维度上的背包,因此我们需要使用二维数组进行循环且背包维度倒序遍历。
【语言总结】
二维数组初始化不够熟练、对字符串的循环遍历不够熟练。