动态规划题型总结 (二)

类型三(简单)

① 走格子 (二维数组):link

题目描述
一个机器人在m×n大小的地图的左上角(起点,下图中的标记“start"的位置)。
机器人每次向下或向右移动。机器人要到达地图的右下角。(终点,下图中的标记“Finish"的位置)。
可以有多少种不同的路径从起点走到终点?
在这里插入图片描述
分析
当path只有一列或者一行的时候,从start到finish只有一种走法;
当path为m*n的path时,可以看做 m-1 行 增加一行 + n-1 列 增加一列;
在这里插入图片描述
即 dp[i][j] = dp[i-1][j] + dp[i][j-1]

int uniquePaths(int m, int n) {
        int  num = 1;
        vector<vector<int>>dp(m,vector<int>(n,1));  //当m或者n等于1时,只有一条路径通往终点,所以为了方便将所有的值初始化为1
        if(m==0 || n == 0)
            return 0;
        for(int i = 1;i<m;i++)
        {
            for(int j = 1;j<n;j++)
            {
                dp[i][j] = dp[i][j-1] + dp[i-1][j] ;  // 通向第[i,j]的路径有两条,分别为[i][j-1] 和 [i-1][j]
            }
        }
        return dp[m-1][n-1];
    }

②最小路径和(走格子升级版):link

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

分析:(dp[i][j]为走到第[i,j]格子上数字之和)
思想同上,第[i,j]格子 可以看成 第[i-1,j]格子向右走一步 或者 第[i,j-1]格子向下走一步 ,取其最小值

	int min(int a ,int b)
    {
        return a<b?a:b;
    }
    int minPathSum(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        if(n == 0||m == 0)
            return 0;
        vector<vector<int>>dp(m,vector<int>(n,0));
        dp[0][0] = grid[0][0];  
        for(int i = 1 ;i<n;i++)    //边界,最上
        {
            dp[0][i] = dp[0][i-1] + grid[0][i];
        }
        for (int i = 1;i<m;i++)   //边界,最左
        {
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }
        for(int i = 1;i<m;i++)
        {
            for(int j = 1;j<n;j++)
            {
                dp[i][j] = min(dp[i-1][j]+grid[i][j],dp[i][j-1]+grid[i][j]);   // 第[i-1,j]格子向右走一步 或者 第[i,j-1]格子向下走一步 的最小值
            }
        }
        return dp[m-1][n-1];
    }

类型四

① 最大子序列之和:link

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
在这里插入图片描述

if  dp[i-1] > 0
	dp[i] = dp[i-1] + nums[i]    //若dp[i-1]>0,则nums[i]带上前面的子序列
else
	dp[i] =  nums[i]                //若dp[i-1]<0,则nums[i]舍弃前面的子序列,从nums[i]重新开始计算

分析:(dp[i]表示下标为i时连续数组的最大和)
当第i个数 + 前i-1个数中连续数组的最大和 的值 小于 第i个数时,说明 前i-1个数中连续数组的最大和 为负数,因此舍去,从第i个数开始累加;即,dp[i] = max(dp[i-1]+nums[i] , nums[i])

	int maxval(int a,int b)
    {
        return a>b?a:b;
    }
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        vector<int>dp(n,0);
        if(n == 0)
            return 0;
        dp[0] = nums[0];
        int max = dp[0];
        for(int i = 1;i<n;i++)
        {
            dp[i]  = maxval((nums[i]+dp[i-1]),nums[i]); // 当dp[i-1]<0,dp[i]=nums[i],相当于之前的序列和清零,从第i个数开始重新计算
            max = max > dp[i] ? max :dp[i];
        }
        return max;
    }

② 最大子序列和 升级版 ---- 乘积最大子序列:link

给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

分析

    由于考虑到 正数 * 负数 = 负数(最大值可能变为最小值)
             负数 * 负数 = 正数(最小值可能变为最大值)
    所以要用两个dp数组来存储过程中产生的最大值和最小值,防止0产生影响,因此在对比过程中要带上原来的数nums[i];
    即,动态方程为:
    dpmin[i] = min(min(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i]),nums[i]);     
    dpmax[i] = max(max(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i]),nums[i]);
    int max(int a, int b)
    {
        return a>b?a:b;
    }
    int min(int a,int b)
    {
        return a<b?a:b;
    }
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        vector<int>dpmax(n,0);
        vector<int>dpmin(n,0);
        dpmax[0] = nums[0];
        dpmin[0] = nums[0];
        int maxnum = nums[0];
        for(int i = 1;i<n;i++)
        {
            dpmin[i] = min(min(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i]),nums[i]) 
            dpmax[i] = max(max(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i]),nums[i]);
            maxnum = maxnum > dpmax[i] ? maxnum:dpmax[i];
        }
        return maxnum;
    }

③ 区域和检索 - 数组不可变:link

给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
示例:
给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()
sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3

分析:(可利用dp[i]来存储前i项的和)
索引i到j元素和 = dp[j] - dp[i-1] //这里用dp[i-1]是因为不包括第i个数

	vector<int>dp;    //设置全局变量
    NumArray(vector<int>& nums) {
        int n = nums.size();
        if(n == 0)            //  若数组为空,则返回
            return ;
        for(int i = 0;i<n;i++)    //初始化dp
        {
            dp.push_back(0);
        }
        dp[0] = nums[0];
        for(int k = 1;k<n;k++)
        {
            dp[k] = dp[k-1] + nums[k];
        }
    }
    
    int sumRange(int i, int j) {
        
        if(i == 0)
            return dp[j];
        else
        return dp[j] - dp[i-1];
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值