首页 > 编程语言 >(算法) ⽬标和————<动态规划>

(算法) ⽬标和————<动态规划>

时间:2024-11-04 17:17:02浏览次数:5  
标签:target nums int sum aim 算法 动态 规划 dp

1. 题⽬链接:494. ⽬标和

2. 题⽬描述:

 

3. 解法(动态规划):

算法思路:

本题可以直接⽤「暴搜」的⽅法解决。但是稍微⽤数学知识分析⼀下,就能转化成我们常⻅的「背 包模型」的问题。 设我们最终选取的结果中,前⾯加 + 号的数字之和为 a ,前⾯加 - 号的数字之和为 b ,整个数组 的总和为 sum ,于是我们有:

        ▪ a + b = sum

        ▪ a - b = target 上⾯两个式⼦消去 b 之后,可以得到 a = (sum + target) / 2 也就是说,我们仅需在 nums 数组中选择⼀些数,将它们凑成和为 (sum + target) / 2 即 可。

问题就变成了 416. 分割等和⼦集 这道题。 我们可以⽤相同的分析模式,来处理这道题。

1. 状态表⽰:

dp[i][j] 表⽰:在前 i 个数中选,总和正好等于 j ,⼀共有多少种选法。

2. 状态转移⽅程:

⽼规矩,根据「最后⼀个位置」的元素,结合题⽬的要求,我们有「选择」最后⼀个元素或者「不 选择」最后⼀个元素两种策略:

        i. 不选 nums[i] :那么我们凑成总和 j 的总⽅案,就要看在前 i - 1 个元素中选,凑 成总和为 j 的⽅案数。根据状态表⽰,此时 dp[i][j] = dp[i - 1][j] ;

        ii. 选择 nums[i] :这种情况下是有前提条件的,此时的 nums[i] 应该是⼩于等于 j 。 因为如果这个元素都⽐要凑成的总和⼤,选择它就没有意义呀。那么我们能够凑成总和为 j 的⽅案数,就要看在前 i - 1 个元素中选,能否凑成总和为 j - nums[i] 。

根据 状态表⽰,此时 dp[i][j] = dp[i - 1][j - nums[i]]

综上所述,两种情况如果存在的话,应该要累加在⼀起。因此,状态转移⽅程为:  dp[i][j] = dp[i - 1][j] if(nums[i - 1] <= j) dp[i][j] = dp[i][j] += dp[i - 1][j - nums[i - 1]]

3. 初始化:

由于需要⽤到「上⼀⾏」的数据,因此我们可以先把第⼀⾏初始化。 第⼀⾏表⽰不选择任何元素,要凑成⽬标和 j 。只有当⽬标和为 0 的时候才能做到,因此第⼀ ⾏仅需初始化第⼀个元素 dp[0][0] = 1

4. 填表顺序:

根据「状态转移⽅程」,我们需要「从上往下」填写每⼀⾏,每⼀⾏的顺序是「⽆所谓的」。

5. 返回值:

根据「状态表⽰」,返回 dp[n][aim] 的值。 其中 n 表⽰数组的⼤⼩, aim 表⽰要凑的⽬标和。

6. 空间优化:

所有的「背包问题」,都可以进⾏空间上的优化。 对于 01背包类型的,我们的优化策略是:

        i. 删掉第⼀维;

        ii. 修改第⼆层循环的遍历顺序即可。 

C++ 优化前的算法代码: 

class Solution 
{
public:
    int findTargetSumWays(vector<int>& nums, int target) 
    {
        //求和
        int sum=0;
        for(auto& key:nums)
        {
            sum+=key;
        }
        //建表
        int m=nums.size(),n=(target+sum)/2;
        //边界情况
        //奇数除二结果不为整数,找不到
        cout<<n<<endl;
        if(n < 0 || (sum + target) % 2) 
        {
            return 0;
        }
        //在前i个元素中选择出和为j的子序列个数
        vector<vector<int>>dp(m+1,vector<int>(n+1));
        //初始化
        dp[0][0]=1;
        //填表
        for(int i=1;i<=m;i++)
        {
            //第一列还未初始化,故j从0开始
            for(int j=0;j<=n;j++)
            {
                //不选
                dp[i][j]=dp[i-1][j];
                //选
                if(j-nums[i-1]>=0)
                {
                    dp[i][j]+=dp[i-1][j-nums[i-1]];
                }
            }
        }
        //返回值
        return dp[m][n];
    }
};

C++ 空间优化后的算法代码:

class Solution 
{
public:
    int findTargetSumWays(vector<int>& nums, int target) 
    {
        //求和
        int sum=0;
        for(auto& key:nums)
        {
            sum+=key;
        }
        //建表
        int m=nums.size(),n=(target+sum)/2;
        //边界情况
        //奇数除二结果不为整数,找不到
        cout<<n<<endl;
        if(n < 0 || (sum + target) % 2) 
        {
            return 0;
        }
        //在前i个元素中选择出和为j的子序列个数
        vector<int>dp(n+1);
        //初始化
        dp[0]=1;
        //填表
        for(int i=1;i<=m;i++)
        {
            //第一列还未初始化,故j从0开始
            for(int j=n;j>=0;j--)
            {
                if(j-nums[i-1]>=0)
                {
                    dp[j]+=dp[j-nums[i-1]];
                }
            }
        }
        //返回值
        return dp[n];
    }
};

Java 优化前的算法代码:

class Solution
{
 public int findTargetSumWays(int[] nums, int target)
 {
	int n = nums.length, sum = 0;
	 for(int x : nums) sum += x;
	 int aim = (sum + target) / 2;
	// 处理?下边界情况
	if(aim < 0 || (sum + target) % 2 == 1) return 0;
	int[][] dp = new int[n + 1][aim + 1];
	dp[0][0] = 1;
	for(int i = 1; i <= n; i++)
		 for(int j = 0; j <= aim; j++){
			 dp[i][j] = dp[i - 1][j];
			 if(j >= nums[i - 1])
				dp[i][j] += dp[i - 1][j - nums[i - 1]];
		 }
	return dp[n][aim];
 }
}

Java 空间优化后的算法代码:

class Solution
{
 public int findTargetSumWays(int[] nums, int target)
 {
	int n = nums.length, sum = 0;
	for(int x : nums) sum += x;
	int aim = (sum + target) / 2;
	 // 处理?下边界情况
	 if(aim < 0 || (sum + target) % 2 == 1) return 0;
	 int[] dp = new int[aim + 1];
	 dp[0] = 1;
	 for(int i = 1; i <= n; i++)
		 for(int j = aim; j >= nums[i - 1]; j--) // 注意修改遍历顺序
			dp[j] += dp[j - nums[i - 1]];
	return dp[aim];
 }
}

标签:target,nums,int,sum,aim,算法,动态,规划,dp
From: https://blog.csdn.net/2301_79580018/article/details/143489436

相关文章

  • NSET or MSET算法--原理解析
    1.背景NSET/MSET是一种非线性的多元预测诊断技术,广泛应用于系统状态估计、故障诊断和预测等领域;相比于传统的线性模型和方法,NSET/MSET能够更好地处理非线性系统,并提供更准确的预测和诊断能力。在早期,MSET融合了模式识别技术和序贯概率比检验方法,主要应用于核电厂信号验证、......
  • 简单介绍机器学习与深度学习以及相关算法
    机器学习算法线性回归解释:线性回归是一种简单的预测算法,它通过寻找输入变量和输出变量之间的线性关系来进行预测。例子:假设你想预测一个房子的价格,可以根据房子的面积(输入)和价格(输出)画一条直线,线性回归就是找到这条最合适的直线。逻辑回归解释:尽管名字中有“回归”,逻......
  • 【算法设计】
    二分治策略2.1最大子数组问题分治算法分治法是一种将问题分解成子问题递归解决的算法思想,用于求解连续最大子数组问题非常合适。该问题的目标是找到数组中连续子数组的最大和。算法解释分解:将数组划分为左右两个子数组,分别求解左右子数组的最大子数组和。合并:最大子数......
  • 烟雾检测识别智慧矿山一体机水仓水位异常识别针对环境不安全因素的算法保障
    在现代矿业生产中,安全始终是最为关键的议题之一。为了提升矿山的安全监管水平,降低生产风险,智慧矿山一体机应运而生。这款设备融合了最新的人工智能技术,为矿山提供了一个全面、高效、智能化的安全解决方案。以下是对智慧矿山一体机的详细介绍,包括其产品特性、环境不安全因素的识别......
  • 越界检测视频分析网关视频智能分析网关周界入侵检测算法:智能安防的新高度
    在当今数字化和智能化快速发展的时代,安全监控系统正经历着一场技术革命。越界检测视频分析网关周界入侵检测算法,作为这场革命的前沿技术之一,正逐渐改变我们对安全监控的认知和实践。本文将详细介绍这一算法的技术特点、应用场景及其在提升安防效率和准确性方面的重要性,展示它是如......
  • 智慧园区算法视频分析服务器烟雾识别智慧园区安防视频监控及动态布控预警方案
    智慧园区安防视频监控及动态布控预警方案是一种综合性的安全管理解决方案,智慧园区算法视频分析服务器通过结合视频监控技术、人工智能算法、大数据分析等技术,实现对工厂区域内人、车、物的全面监控和管理。一、需求和目标1、系统建设目标:目标是构建一个重点区域的动态人脸识别......
  • CSS:实现动态流光线条效果/动态流光线条颜色渐变效果
    需求分析需要实现类似下图中的动态流光线条效果:思路提到这种动态绘制矢量图形的需求,一般会想到使用canvas;由于笔者不太熟悉canvas动画也可以考虑用CSS来实现,这里先记录使用CSS实现此效果的尝试过程:①实现一条带有静态“流光”效果的边,参考CSS渐变背景;②实现静态线条的“流光......
  • 算法专题:哈希表
    目录1、1.两数之和1.1算法原理1.2算法代码2、面试题01.02.判定是否互为字符重排2.1算法原理 2.2算法代码3、217.存在重复元素3.1算法原理  3.2算法代码4、219.存在重复元素II4.1算法原理4.2算法代码5、49.字母异位词分组5.1算法原理 5.2算......
  • 基于改进多目标灰狼优化算法的考虑V2G技术的风、光、荷、储微网多目标日前优化调度研
    ......
  • 基于改进多目标灰狼优化算法的考虑V2G技术的风、光、荷、储微网多目标日前优化调度研
    ......