46. 携带研究材料
题解:代码随想录
状态:需要多复习
思路
0-1背包问题:
dp[i][j]:i表示物品种类,j表示背包重量
或者 dp[j]:j表示背包重量
代码(二维dp数组)
时间复杂度:O(M*N) 空间复杂度:O(M*N)
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int M = in.nextInt();
int N = in.nextInt();
int[] space = new int[M];
int[] value = new int[M];
for(int i = 0; i < M; i++){
space[i] = in.nextInt();
}
for(int i = 0; i < M; i++){
value[i] = in.nextInt();
}
int[][] dp = new int[M][N + 1];
for(int i = space[0]; i <= N; i++){
dp[0][i] = value[0];
}
for(int i = 1; i < M; i++){
for(int j = 1; j <= N; j++){
if(space[i] > j) dp[i][j] = dp[i - 1][j];
else dp[i][j] = Math.max(
dp[i - 1][j],
dp[i - 1][j - space[i]] + value[i]
);
}
}
System.out.println(dp[M - 1][N]);
}
}
代码(一维dp数组)
时间复杂度:O(N) 空间复杂度:O(N)
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int M = in.nextInt();
int N = in.nextInt();
int[] space = new int[M];
int[] value = new int[M];
for(int i = 0; i < M; i++){
space[i] = in.nextInt();
}
for(int i = 0; i < M; i++){
value[i] = in.nextInt();
}
int[] dp = new int[N + 1];
for(int i = 0; i < M; i++){
for(int j = N; j >= space[i]; j--){
dp[j] = Math.max(dp[j], dp[j - space[i]] + value[i]);
}
}
System.out.println(dp[N]);
}
416. 分割等和子集
题解:代码随想录
状态:AC
思路
- 先统计数组总和,若总和非偶数,则一定无法分割
- 之后就是0-1背包问题,不过是要判断背包是否装满
- 其中要加上一个剪枝操作
if(dp[sum] == sum) return true;,提早结束循环
代码
时间复杂度:O(n*sum) 空间复杂度:O(sum)
class Solution {
public boolean canPartition(int[] nums) {
int sum = 0;
for(int num : nums){
sum += num;
}
if(sum % 2 != 0) return false;
sum /= 2;
int[] dp = new int[sum + 1];
for(int i = 0; i < nums.length; i++){
for(int j = sum; j >= nums[i]; j--){
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
}
// 剪枝操作
if(dp[sum] == sum) return true;
}
return dp[sum] == sum;
}
}
1049.最后一块石头的重量II
题目:1049. 最后一块石头的重量 II - 力扣(LeetCode)
题解:代码随想录
状态:思路需要看一下
思路
是416. 分割等和子集的变体,尽可能将石头分成重量相等的两组
代码
时间复杂度:O(n*sum) 空间复杂度:O(sum)
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = 0;
for(int stone : stones){
sum += stone;
}
int target = sum / 2;
int[] dp = new int[target + 1];
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 - 2 * dp[target];
}
}
494.目标和
题解:代码随想录
状态:需要多复习
思路
从计算最大容量,改为计算填满背包的可能数
- 注意
(target + sum) % 2 == 1时,无解 - 注意
Math.abs(target) > sum时,无解
例如:dp[j],j 为5,
- 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
- 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
- 已经有一个3(nums[i]) 的话,有 dp[2]种方法 凑成 容量为5的背包
- 已经有一个4(nums[i]) 的话,有 dp[1]种方法 凑成 容量为5的背包
- 已经有一个5 (nums[i])的话,有 dp[0]种方法 凑成 容量为5的背包
那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。
代码
时间复杂度:O(N*size) 空间复杂度:O(size)
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for(int num : nums){
sum += num;
}
if((target + sum) % 2 == 1) return 0;
if(Math.abs(target) > sum) return 0;
int size = (sum + target) / 2;
int[] dp = new int[size + 1];
dp[0] = 1;
for(int i = 0; i < nums.length; i++){
for(int j = size; j >= nums[i]; j--){
dp[j] += dp[j - nums[i]];
}
}
return dp[size];
}
}
474.一和零
题解:代码随想录
状态:完全没思路
思路
- dp数组定义:dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j] 。
- 递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
- dp数组初始化:01背包的dp数组初始化为0
- 遍历顺序:内外曾均为倒序
代码
时间复杂度:O() 空间复杂度:O()
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
//dp[i][j]表示i个0和j个1时的最大子集
int[][] dp = new int[m + 1][n + 1];
int oneNum, zeroNum;
for (String str : strs) {
oneNum = 0;
zeroNum = 0;
for (char ch : str.toCharArray()) {
if (ch == '0') {
zeroNum++;
} else {
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];
}
}