随便几道动态规划

动态规划题目最重要的三步:①想清楚dp数组的含义②从后往前,从下往上想③写出递归方程

最长连续递增序列

给定一个未经排序的整数数组,找到最长且连续的的递增序列,并返回该序列的长度。

示例 1:

输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。 

示例 2:

输入: [2,2,2,2,2]
输出: 1
解释: 最长连续递增序列是 [2], 长度为1。

**注意:**数组长度不会超过10000。

思路:dp[i]为以数组元素nums[i]为结尾的最长递增子序列。从后往前看,默认已经取得dp[i-1],则只需要判断nums[i]是否能和dp[i-1]组成递增子序列,就可以判断dp[i]的值,即:

dp[i]=(nums[i]>nums[i-1])>dp[i-1]+1:1;

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        vector<int>dp(nums.size(),1);
        for(int i=1;i<nums.size();i++)
        {
            if(nums[i]>nums[i-1])
            {
                dp[i]=dp[i-1]+1;
            }
        }
        int max=0;
        for(int i=0;i<dp.size();i++)
        {
            max=(max<dp[i])?dp[i]:max;
        }
        return max;
    }
};

买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

思路:dp[i]为第i天卖出时的最大收益。则思考的是应该在前i天股票价格最低时买入。这里做一个Min。每当访问一个新的数组元素时就将它与Min进行比较,然后更新Min。dp[i]=s[i]-Min。(感觉这种做法和动态规划没啥关系)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()==0)
        {
            return 0;
        }
        vector<int> dp(prices.size(),0);
        int min=prices[0];
        for(int i=0;i<prices.size();i++)
        {
            min=(prices[i]<min)?prices[i]:min;
            if(prices[i]-min>0)
            {
                dp[i]=prices[i]-min;
            }
        }
        int max=dp[0];
      
        for(int i=0;i<dp.size();i++)
        {
            //cout<<dp[i]<<endl;
            if(max<dp[i])
            {
                max=dp[i];
                
            }
        }
        return max;
        
    }
};

最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

思路:dp[i]为以nums[i]为结尾的和最大的子数组的和。同样假设已经获取到dp[i-1],那么只需要判断nums[i]+dp[i-1]是否比nums[i]独自一个来的更大,是的话就更新dp[i],即:

dp[i]=(nums[i]+dp[i-1]>nums[i])?nums[i]+dp[i-1]:nums[i];

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.size()==0)
        {
            return -2147483648;
        }
        vector<int>dp(nums.size());
        dp[0]=nums[0];
        for(int i=1;i<nums.size();i++)
        {
            if(dp[i-1]+nums[i]>nums[i])
            {
                dp[i]=dp[i-1]+nums[i];
            }
            else
            {
                dp[i]=nums[i];
            }
            //cout<<dp[i]<<endl;
        }
        int max=dp[0];
        for(int i=0;i<nums.size();i++)
        {
            if(max<dp[i])
            {
                max=dp[i];
            }
        }
        return max;
    }
};

最大正方形

在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。

示例:

输入: 

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

输出: 4

思路:dp[i] [j]为以(i,j)为右下角的最大正方形的面积。*(边长更好,我是写完才发现别人用的是边长,不过方法其实都一样)*这里首先可以断定几件事:

(1)左侧和顶侧的dp[i] [j]只和它们自身的值相等,即当i=0或j=0时,dp[i] [j]=map[i] [j]。

(2)如果map[i] [j]==0则dp[i] [j]=0。

在已知上面的先决条件以后我们要思考的是,在map[i] [j]==1的情况下,怎么确定dp[i] [j]的值?

首先我们可以知道dp[i] [j]的值受到**dp[i-1] [j]、dp[i] [j-1]、dp[i-1] [j-1]**的影响。

我之前写的时候只考虑了dp[i-1] [j]、dp[i] [j-1],忘记了还有

1 1 1

1 0 1

1 1 1

这种可能。

dp[i] [j] 代表最大和的边长的动态规划方程为:dp[i] [j]=min(min(dp[i-1] [j], dp[i] [j-1]), dp[i-1] [j-1])+1
有点难描述为什么这样,我开始想的时候没考虑左上角,只是觉得能走多远受到两边长的控制,取短的那边就行了orz,看到答案才想到,如果只看两边长就有可能是空心结构,事实上两边长只能决定最大圈多大的地,能不能圈起来要看dp[i-1] [j-1]有多大。

这题的难点就是动态规划方程了。

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        
        int dp[1000][1000];
        int max=0;
        for(int i=0;i<matrix.size();i++)
        {
            //cout<<i<<endl;
            for(int j=0;j<matrix[0].size();j++)
            {
                //cout<<" "<<j<<endl;
                if(i==0||j==0)
                {
                   
                    dp[i][j]=matrix[i][j]-'0';
                }
                else
                {
                    if(matrix[i][j]-'0')
                    {
                        int temp=(dp[i-1][j]<dp[i][j-1])?dp[i-1][j]:dp[i][j-1];
                        temp=(temp<dp[i-1][j-1])?temp:dp[i-1][j-1];
                        if(temp==0)
                        {
                            dp[i][j]=1;
                        }
                        else
                        {
                            
                            int side=sqrt(temp);
                            dp[i][j]=(side+1)*(side+1);
                        }
                    }
                    else
                    {
                        dp[i][j]=0;
                    }
                }
                max=(max<dp[i][j])?dp[i][j]:max;
                
            }
        }
        return max;
    }
};
展开阅读全文
©️2020 CSDN 皮肤主题: 游动-白 设计师: 上身试试 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值