动态规划01背包练习
01背包的题目:
416. 分割等和子集
类加所有数字为sum,如果sum为奇数返回false
然后试着将所有数字分成两份,如果其中有一份能达到sum/2 ,返回true,否则为false。
DFS, 暴力过不了所有的点
public static boolean canPart( int[] nums ) {
int sum = 0;
for ( Integer i : nums ) {
sum += i;
}
return dfs( nums, 0, 0, sum );
}
private static boolean dfs( int[] nums, int index, int sum, int target ) {
//base case
if ( nums.length == index ) {
if ( sum == target ) {
return true;
} else {
return false;
}
}
//对于任意一个数,可与选或者不选
return dfs( nums, index + 1, sum + nums[index], target )
||
dfs( nums, index + 1, sum, target );
}
// 作者:winlsr
// 链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/dfs-dfs-bei-wang-lu-01bei-bao-dong-tai-g-4j1t/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
暴力+记忆化搜索
// 备忘录:也可用一个二维数组,一维表示元素和sum,一维表示当前索引index
private Map<String, Boolean> map = new HashMap<>();
private boolean dfsAndMap( int[] nums, int index, int sum, int target ) {
if ( nums.length == index ) {
if ( sum == target ) {
return true;
} else {
return false;
}
}
//描述一个子问题的两个变量是 sum 和 index,组成 key 字符串
String key = sum + "&" + index;
if ( map.containsKey( key ) ) {
return map.get( key );
}
boolean ret = dfs( nums, index + 1, sum + nums[index], target ) ||
dfs( nums, index + 1, sum, target );
map.put( key, ret );
return ret;
}
动态规划
等价关系是这样的:
从 [0…i] 这个区间里抽出若干个元素能够填满 sum / 2 的背包,
就一定可以把区间为全部、和为 sum 的整数拆分成两个相等的子集【1】【2】
dp[ i ][ j ]
的意思是,选 i
个数字,刚好等于 j
因此dp [ i ] [ j ]
有两种状态,true或者false。
public boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) {
sum += num;
}
if (sum % 2 == 1) {
return false;
}
int target = sum / 2;
int n = nums.length;
boolean[][] dp = new boolean[n + 1][target + 1];
dp[0][0] = true;
for(int i=1;i<=n;i++){
for(int j=1;j<=target;j++){
// 不选,则当前状态和上一层状态相同
if(j < nums[i-1]){
dp[i][j]=dp[i-1][j];
}else{
// 选了当前元素, 上一层布尔值 || 选了当前元素的布尔值
dp[i][j]=dp[i-1][j] || dp[i-1][j-nums[i-1]];
}
}
}
return dp[n][target];
}
一维优化
public boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) {
sum += num;
}
if (sum % 2 == 1) {
return false;
}
int target = sum / 2;
int n = nums.length;
boolean[] dp = new boolean[target + 1];
dp[0] = true;
for(int i=1;i<=n;i++){
for(int j=target;j>=nums[i-1];j--){
dp[j]= dp[j-nums[i-1]] || dp[j];
}
}
return dp[target];
}
1049. 最后一块石头的重量 II
动态规划
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = 0;
for (int s : stones) {
sum += s;
}
int target = sum / 2;
//初始化,dp[i][j]为可以放0-i物品,背包容量为j的情况下背包中的最大价值
int[][] dp = new int[stones.length][target + 1];
//dp[i][0]默认初始化为0
//dp[0][j]取决于stones[0]
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 - 1][j] 放:dp[i - 1][j - stones[i]] + 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];
}
}
}
// System.out.println(dp[stones.length - 1][target]);
return (sum - dp[stones.length - 1][target]) - dp[stones.length - 1][target];
}
}
494. 目标和
DFS 搜索
int res = 0;
public int findTargetSumWaysByDFS( int[] nums, int target ) {
int sum = 0;
for ( Integer i : nums ) {
sum += i;
}
if ( sum < target ) {
return 0;
}
dfs( nums, target, 0, 0 );
return res;
}
public void dfs( int[] nums, int target, int sum, int index ) {
if ( index == nums.length ) {
if ( sum == target ) {
res++;
return;
}
}
if ( index != nums.length ) {
dfs( nums, target, sum - nums[index], index + 1 );
dfs( nums, target, sum + nums[index], index + 1 );
}
}
DFS 方法二
class Solution {
public int findTargetSumWays(int[] nums, int t) {
return dfs(nums, t, 0, 0);
}
int dfs(int[] nums, int t, int u, int cur) {
if (u == nums.length) {
return cur == t ? 1 : 0;
}
int left = dfs(nums, t, u + 1, cur + nums[u]);
int right = dfs(nums, t, u + 1, cur - nums[u]);
return left + right;
}
}
作者:AC_OIer
链接:https://leetcode-cn.com/problems/target-sum/solution/gong-shui-san-xie-yi-ti-si-jie-dfs-ji-yi-et5b/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
二维动态规划
目标和:给数组里的每个数字添加正负号得到target
数组和sum
,目标和targer
,
数字可以分为两堆,左边组合 x
, 右边组合 y
,
则
x + y = sum
, x - y = targer
,
那么
x + x = sum + targer
x = ( target + sum ) / 2
【3】
target是固定的,sum是固定的,y 就可以求出来
此时问题就是在集合nums中找出和为x
的组合。
也就是说问题有解需要满足两个条件
- 数字全部加起来大于 target
- (sum + target) % 2 != 0 要不然无法找到相应的左右组合使得题意成立。
定义dp[ i ] [ j ]
表示, 选 0-i 个数,恰好使得他们的和为 j 的方法数有 dp[ i ] [ j ]
种。
初试化 dp[ 0 ] [ 0 ] = 1
表示一个数都不选,他们的和恰好为0 的方法数有 1 种
对于第i
个数字,我们有两种选择:
-
选择将第
i
个数字放入左边的组合,
此时,数字还剩i - 1
个,背包的容量需要减去nums [ i ]
(此处相当于要凑j
,还差j-nums[i]
, 而这个信息在dp [ i - 1 ] [ j - nums [ i ] ]
存放着)递推式为
dp [ i ] [ j ] = dp [ i - 1 ] [ j - nums [ i ] ]
; -
选择放弃第i个数字,直接来到第i - 1个数字,
那么能满足和为 j 的方式就是上一行的值(dp[ i - 1] [ j ]
)递推式为
dp [ i ] [ j ] = dp [ i - 1 ] [ j ]
; -
当背包的容量小于第
i
个数字时,即j
<nums[ i ]
,无法将第i
个数字放入背包,只能跳过,递推式同2 【4】
此处相当于,剩下要凑的 数字 j
已经比 其他数字小了,无法
所以 dp [ i ] [ j ] = dp [ i - 1 ] [ j ]
public int findTargetSumWays( int[] nums, int target ) {
int res = 0;
int sum = 0;
for ( int i : nums ) {
sum += i;
}
if ( Math.abs( target ) > sum || (sum + target) % 2 != 0 ) {
return 0;
}
int mid = (sum + target) / 2;
int N = nums.length;
int[][] dp = new int[N + 1][mid + 1];
dp[0][0] = 1;
for ( int i = 1; i <= N; i++ ) {
for ( int j = 0; j <= mid; j++ ) {
if ( j >= nums[i - 1] ) {
// 两种选择的结果之和
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]];
} else {
// 背包的空间不足,只能选择不装物品 i
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[N][mid];
}
一维优化
public int findTargetSumWays(int[] nums, int target) {
int res = 0;
int sum = 0;
for (int i : nums) {
sum += i;
}
if (Math.abs(target) > sum || (sum + target ) % 2 != 0 ) {
return 0;
}
int mid = (sum + target) / 2;
int N = nums.length;
int []dp = new int [mid+1];
dp[0]=1;
for (int i = 1; i <= N; i++) {
for (int j = mid; j>=nums[i-1]; j--) {
dp[j] = dp[j] + dp[j-nums[i-1]];
}
}
return dp[mid];
}
474. 一和零
暴力超时
int res = 0 ;
public int findMaxForm(String[] strs, int m, int n) {
List<String> path = new ArrayList<>();
get(strs,0,m,n,0,0,path);
return res;
}
public void get(String[] strs,int index, int m, int n,int numM,int numN,List<String> path ){
if(numM <=m && numN<= n){
res = Math.max(res,path.size());
}else{
return;
}
for(int i=index;i<strs.length;i++){
path.add(strs[index]);
int[] cur = count(strs[i]);
get(strs,i+1,m,n,cur[0]+numM,cur[1]+numN,path);
path.remove(path.size()-1);
}
}
public int[] count(String s){
int [] res = new int[2];
int i = 0;
int len = s.length();
while(i<len){
if(s.charAt(i) == '0'){
res[0]++;
}else{
res[1]++;
}
i++;
}
return res;
}
动态规划
参考【5】
public class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int len = strs.length;
int[][][] dp = new int[len + 1][m + 1][n + 1];
for (int i = 1; i <= len; i++) {
// 注意:有一位偏移
int[] count = countZeroAndOne(strs[i - 1]);
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= n; k++) {
// 先把上一行抄下来
dp[i][j][k] = dp[i - 1][j][k];
int zeros = count[0];
int ones = count[1];
if (j >= zeros && k >= ones) {
dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - zeros][k - ones] + 1);
}
}
}
}
return dp[len][m][n];
}
private int[] countZeroAndOne(String str) {
int[] cnt = new int[2];
for (char c : str.toCharArray()) {
cnt[c - '0']++;
}
return cnt;
}
}
空间优化
public class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m + 1][n + 1];
dp[0][0] = 0;
for (String s : strs) {
int[] zeroAndOne = calcZeroAndOne(s);
int zeros = zeroAndOne[0];
int ones = zeroAndOne[1];
for (int i = m; i >= zeros; i--) {
for (int j = n; j >= ones; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1);
}
}
}
return dp[m][n];
}
private int[] calcZeroAndOne(String str) {
int[] res = new int[2];
for (char c : str.toCharArray()) {
res[c - '0']++;
}
return res;
}
}
【1】https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/782571
【2】https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
【3】https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/yi-pian-wen-zhang-chi-tou-bei-bao-wen-ti-a7dd/
【4】https://leetcode-cn.com/problems/target-sum/solution/hen-xiang-xi-de-zhuan-hua-wei-0-1bei-bao-irvy/
【5】https://leetcode-cn.com/problems/ones-and-zeroes/solution/dong-tai-gui-hua-zhuan-huan-wei-0-1-bei-bao-wen-ti/