子集划分是个什么东西?背包问题->动态规划?

子集划分是个什么东西?背包问题->动态规划?

子集划分问题抽取

在这里插入图片描述
以上题目来自力扣494.目标和,本篇文章的重点在于提取出其中的一种类型问题,称为子集划分问题

所谓子集划分问题,是一个典型的背包问题,而背包问题,使用的是动态规划来解决。此题可以使用回溯方法来做,但由于一些重复的计算,时间复杂度会很高,因此可以转化为背包问题从而使用动态规划来解决来降低复杂度。

子集划分问题解决思路

子集划分问题,其实就是我们把nums数组分成两个子集A和B,分别代表分配+和分配-的数,那么他们和S会存在以下关系:

//加数和-减数和等于目标值
sum(A)-sum(B)=S
sum(A)=S+sum(B);
//两边同时加上A的和
sum(A)+sum(A)=S+sum(B)+sum(A);
2*sum(A)=S+sum(nums)

那我们可以推出sum(A)=(target + sum(nums) ) / 2,也就把原问题转化为,在nums中能寻找到多少个子集,满足子集和等于目标值+nums集合和的一半
那么我们可以实现以下两个函数来解决此类问题:

//计算nums中有几个子集的和为sum
int howManySubsets(int[] nums,int sum){
    
    
	 int n=nums.length;
	 //dp[i][j]=x表示在前i个物品中选择,若当前背包容量为j,则最多有x种方法能恰好装满背包 
     int[][] dp=new int[n+1][sum+1];
     for(int i=0;i<=n;i++){
    
    
         //dp[..][0]=1 如果背包最大载重为0 那什么都不装是一种
         //dp[0][..]=0 没有物品,则无法装包
         dp[i][0]=1;
     }
     for(int i=1;i<=n;i++){
    
    
        for(int j=0;j<=sum;j++){
    
    
           if(j>=nums[i-1]){
    
    
                 //等于放入与不放入二者之和
                 dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i-1]];
             }else{
    
    
                 //背包空间不足 只能不放
                 dp[i][j]=dp[i-1][j];
             }
         }
     }
     return dp[n][sum];
}
//函数
int findTargetSumWays(int[] nums,int S){
    
    
	int sum=0;
	for(int n:nums){
    
    
		sum+=n;
	}
	if(sum<S||(sum+target)%2==1){
    
    
		return 0;
	}
	return howManySubsets(nums,(sum+target)/2;
	
}

其他题目

像其他问题也可以使用这种解法,例如416.分割等和子集
在这里插入图片描述

class Solution {
    
    
    public boolean canPartition(int[] nums) {
    
    
        int sum=0;
        for(int num:nums){
    
    
            sum+=num;
        }
        if(sum%2==1) return false;
        int target=sum/2;
        boolean[][] dp=new boolean[nums.length][target+1];
        //第一个只能装满容积为第一个大小的背包
        if(nums[0]<=target){
    
    
            dp[0][nums[0]]=true;
        }
        for(int i=1;i<nums.length;i++){
    
    
            for(int j=0;j<=target;j++){
    
    
                dp[i][j]=dp[i-1][j];

                if(nums[i]==j){
    
    
                    dp[i][j]=true;
                    continue;
                }
                if(nums[i]<j){
    
    
                    dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i]];
                }
            }
        }
        return dp[nums.length-1][target];
    }
}

猜你喜欢

转载自blog.csdn.net/fucccck_ly/article/details/109674876