动态规划

53 Maximum Subarray  连续最大子数列

描述:寻找一个数列中连续的几个数,这几个数的和最大
思路1: 举例分析数据规律

例如数组[1,-2,3,10,-4,7,2,-5]

//1.初始的cusum=1,sum=1,将-2加到cusum上得到-1,比1小,sum不更新。

//2.cusum小于0,更换为3,因为3比sum大,sum更新为3;

//3.cusum大于零,加10得13,13比sum大,sum更新为13.

依次按此方法,得到连续子数列最大值。

整理一下思路就是:

连续子数列的最大和是从一个候选数列中得到的,可以选这样一个候选数列:每一个标号结尾的最大数列,用cursum表示,cursum[i]表示以当前节点结尾的最大值,这个值可以通过遍历递进求得,如果之前的cursum[i-1]>0,那么cursum[i]=cursum[i-1]+a[i],如果cursum[i-1]<0,那么cursum[i]=a[i].(这里可以保证数列是连续的,以为并没有管当前的a[i]是否大于0,都做了加和)。这样就求出了cursum,sum只是保存cursum中最大的那个。

class Solution {
public:
    int maxSubArray(vector& nums) {
        int size=nums.size();
        if(size==0) return 0;
        int cursum=nums[0];
        int sum=nums[0];
        for(int i=1;i
思路2:和思路一是一致的,也是先选择这样一组数列,他们是以当前节点结尾的最大和,然后在里面找到最大的那个,只不过这里把cursum作为一个数列,能够显示出这个思路的规律来。

class Solution {
public:
    int maxSubArray(vector& nums) {
        int size=nums.size();
        if(size==0) return 0;
        vector cursum(size,nums[0]);
        for(int i=1;i
还有其他的方法,参考 http://blog.csdn.net/sgbfblog/article/details/8032464

121 Best Time to Buy and Sell Stock40.9%Easy

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Example 1:

Input: [7, 1, 5, 3, 6, 4]
Output: 5

max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price)
思路:依次求出当前节点卖出时的股票收益curBenif,所以要求出之前的最小股票价格,用minStack表示。用benif表示最大的值。
class Solution {
public:
    int maxProfit(vector& prices) {
        int size=prices.size();
        //前面处理开头准备工作
        if(size<=1) return 0;
        int sum=prices[1]-prices[0];
        int cur=prices[1]-prices[0];
        int minStock=prices[0]cur) cur=temp;
            if(prices[i]

另外参考之前我写的 http://blog.csdn.net/m0_37693059/article/details/70291220 


198 House Robber38.7%Easy

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

理解题意:相邻的房间不能都被偷,不是说一个房间的两侧不能被偷。

使用动态规划:题目简化为只有一个房间时的结果,然后依次增加后面的房间。假设前面已经求得了最好的结果。那么当考虑当前的房间时,当前的房间偷或者不偷。当选择到当前房间时,一个选择是:当前房间之前的房间不偷,偷当前的房间。另一个选择是,当前的房间不偷,则结果等于之前房间的结果。递推公式是:

dp[i]=max(dp[i-1],dp[i-2]+nums[i])

本题还需要考虑数列的大小,因为地推公式包含三个值,当数列只有一个,两个的时候要特殊处理。

class Solution {
public:
    int rob(vector& nums) {
        int size=nums.size();
        if(size==0) return 0;
        vector dp(size,nums[0]);
        if(size==1) return dp[0];
        dp[1]=nums[1]>nums[0]?nums[1]:dp[0];
        if(size==2) return dp[1];
        for(int i=2;itemp2?temp1:temp2;
        }
        return dp[size-1];
    }
    
};
第二种: 不需要设置一个矩阵
class Solution {
public:
    int rob(vector& nums) {
        int n=nums.size();
        int pre=0,cur=0;
        for(int i=0;i

213. House Robber II

Note: This is an extension of House Robber.

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

上题的变种,这道题改变的条件是,房子首尾连接成了一个圈。

思路:分别假设第一个和最后一个房子没有偷,按照上题的解法求解,取两者的最大值

class Solution {
public:
    int rob(vector& nums) {
        int n=nums.size();
        if(1==n) return nums[0]; 
        return max(rob(nums,0,n-2),rob(nums,1,n-1));
    }
    //hos robber 1
     int rob(vector& nums,int lo,int hi){
        int pre=0,cur=0,temp=0;
        for(int i=lo;i<=hi;i++)
        {
            temp=cur;
            cur=max(nums[i]+pre,cur);
            pre=temp;
        }
        return cur;
    }
};

91 Decode Ways19.5%Medium


思路:使用动态规划,dp[i]=dp[i-1]+1,这个题目关键考虑0怎么处理
错误编程:
class Solution {
public:
    int numDecodings(string s) {
        //注意输入的数可能不是1-9之间的数,这时跳过这个数不讨论
        int size=s.size();
        if(size==0) return 0; 
        vector dp(size,0);
        if(s[0]>='1'&&s[0]<='9') dp[0]=1;
        else return 0;
        for(int i=1;i'9') return 0;
            else if(s[i-1]=='1'||(s[i-1]=='2'&&s[i]<='6')) dp[i]=dp[i-1]+1;
            else dp[i]=dp[i-1];
        }
               return dp[size-1];
        
    }
};

正确解法:
class Solution {
public:
    int numDecodings(string s) {
        //注意输入的数可能不是1-9之间的数,这时跳过这个数不讨论
        int n=s.size();
        if(!n) return 0;
        vector res(n+1,0);
        res[0]=1;//空字符串有一个
        res[1]=(s[0]=='0'?0:1);
        for(int i=2;i<=n;i++)
        {
            if(std::stoi(s.substr(i-1,1))!=0) 
                res[i]+=res[i-1];
            //注意两个if是并列的,因为讨论的末尾不同,即使前面有相同的结果,考虑到最后末尾的不同,也是不同的结果,因此是加和
            if(std::stoi(s.substr(i-2,2))>=10&&std::stoi(s.substr(i-2,2))<=26)
                res[i]+=res[i-2];
        }
        return res[n];
    }
};

62 Unique Paths Medium  寻找所有的路径 简单的动态规划问题

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

How many possible unique paths are there?


Above is a 3 x 7 grid. How many possible unique paths are there?

Note: m and n will be at most 100.

思路:dp[i][j]=dp[i-1][j]+dp[i][j-1] 

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector> dp(m,vector(n,0));
        if(m==0&&n==0) return 0;
        dp[0][0]=1;
        for(int i=0;i

63 Unique Paths II Medium 绝对路径2

此题与上一题几乎一样。
class Solution {
public:
    int uniquePathsWithObstacles(vector>& obstacleGrid) {
        int m=obstacleGrid.size();
        if(m==0) return 0;
        int n=obstacleGrid[0].size();
        if(n==0) return 0;
        vector> dp(m,vector(n,0));
        if(obstacleGrid[0][0]==1) return 0;//注意障碍可能是在入口处
            dp[0][0]=1;
        for(int i=0;i
//这里最好是从1开始,因为当矩阵只有1个时,会出错
{ if(obstacleGrid[0][i]==1) break; dp[0][i]=1; } for(int i=1;i
 
   
 
   
 
   
 
   
 
   
 
   
 
   
 
   
 
  

64 Minimum Path SumMedium 最小路径和

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

一个矩阵包含一些数,要求找到从左上角到右下角和的最小值
递推公式:sum[i][j]=min(sum[i-1][j],sum[i][j-1])+grid[i][j]

class Solution {
public:
    int minPathSum(vector>& grid) {
        int m=grid.size();
        if(m==0) return 0;
        int n=grid[0].size();
        if(n==0) return 0;
        vector> sum(m,vector(n,0));
        sum[0][0]=grid[0][0];
        for(int i=1;i

221 Maximal Square28.6%Medium

Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.

For example, given the following matrix:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 4.
借鉴正确解答:
(1) 动态规划
\text{dp}(i,\ j) = \min \big( \text{dp}(i-1,\ j),\ \text{dp}(i-1,\ j-1),\ \text{dp}(i,\ j-1) \big) + 1. 动态规划_第1张图片 dp ( i ,  j ) =m in ( dp ( i 1 ,  j ) ,  dp ( i 1 ,  j 1 ) ,  dp ( i ,  j 1 ) ) + 1 .

class Solution {
public:
    int maximalSquare(vector>& matrix) {
        int n=matrix.size();
        if(!n) return 0;
        int m=matrix[0].size();
        int res=0;
        vector> dp(n+1,vector(m+1,0));//技巧,多设置一排一列数组,就不需要考虑边界值的问题了
        for(int i=1;i

改善,改善空间复杂度:
动态规划_第2张图片
public class Solution {
    public int maximalSquare(char[][] matrix) {
        int rows = matrix.length, cols = rows > 0 ? matrix[0].length : 0;
        int[] dp = new int[cols + 1];
        int maxsqlen = 0, prev = 0;
        for (int i = 1; i <= rows; i++) {
            for (int j = 1; j <= cols; j++) {
                int temp = dp[j];
                if (matrix[i - 1][j - 1] == '1') {
                    dp[j] = Math.min(Math.min(dp[j - 1], prev), dp[j]) + 1;
                    maxsqlen = Math.max(maxsqlen, dp[j]);
                } else {
                    dp[j] = 0;
                }
                prev = temp;
            }
        }
        return maxsqlen * maxsqlen;
    }
}

85 Maximal Rectangle27.6%Hard

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.

For example, given the following matrix:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 6.

152 Maximum Product Subarray25.5%Medium

Find the contiguous subarray within an array (containing at least one number) which has the largest product.

For example, given the array [2,3,-2,4],
the contiguous subarray [2,3] has the largest product = 6.

寻找给定数组的一个连续子数组,使得这个数组铬元素的乘积最大。
大神解法1:
首先数组中可能有0.先不考虑0的存在
问题的关键时如何考虑负数,因为负数的个数可能时偶数也可能是奇数,如果是偶数个,那么所有的数相乘是最大的,如果有奇数个负数,有两种抵消方式,抵消之后的剩余的负数一定在边缘,因此,从两头分别相乘取最大值即可。
如果存在0值,从两头×时,遇到0,之前的最大值保存好,0之后的数重新相乘计算;
0的存在相当于将数组分成多块,而从两头×,还是将每一个字块的奇数个负数的情况考虑进来了,只要把最大值随时保存好,就不会丢失这个最大值。
代码如下:
class Solution {
public:
    int maxProduct(vector& nums) {
        int result=std::numeric_limits::min();
        int n=nums.size();
        int front=1;
        int back=1;
        for(int i=0;i

大神解法2:动态规划
思路还是依次求解以当前节点为结尾的连续数相乘的最大值,用一个变量保存遍历过程中的最大值,如果当前的值不比这个数大,那么就不更新这个变量。
问题的关键是如何求当前以当前节点结尾的连续数相乘的最大值,存在的主要问题是,当前节点的最大值不仅仅与前面节点递推求得的最大值有关,还可能与之前递归求解的最小值有关,例如:
前面求得最大值和最小值,如果当前值是负数,那么当前值乘以之前的最小值一定比乘以最大值要大,并且,有可能当前值自己比前面讨论的两个值都大。
如果当前值是负数,那么之后用到的最大值和最小值正好调过来,因此使用swap交换。
当出现0值时,所有的值都变成了0,代码如下:
class Solution {
public:
    int maxProduct(vector& nums) {
        int result=std::numeric_limits::min();
        int n=nums.size();
        if(n==0) return 0;
        result=nums[0];
        for(int i=1,maxnum=result,minnum=result;i


120 Triangle 33.6% Medium 寻找三角形矩阵的最小路径

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

For example, given the following triangle

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

要求尽量少用额外空间

对于当前的节点triangle[i][j]只能走到triangle[i+1][j]或者triangle[i+1][j-1]。使用一维矩阵,随时更新。
需要注意的几点是:
(1)初始值的给定。初始化可能覆盖掉原值。这时使用 std::numeric_limits::max()。
(2)三角形类的题目都要考虑新旧值的覆盖问题。注意第二个循环要从后往前循环。因为当前值i和更新前的当前值i以及前面i-1的值决定,如果按照从前往后的顺序,则前面的值已经更新,之前旧的值被覆盖掉了。
class Solution {
public:
    int minimumTotal(vector>& triangle) {
        int n=triangle.size();
        if(n==0) return 0;
        if(n==1) return triangle[0][0];
        vector result(n,std::numeric_limits::max());//记住这种形式表示
        result[0]=triangle[0][0];
        for(int i=1;i=0;j--)
            {
                int tempMin=result[j];
                if(j>0&&result[j-1]

139 Word Break29.7%Medium

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. You may assume the dictionary does not contain duplicate words.

For example, given
s = "leetcode",
dict = ["leet", "code"].

Return true because "leetcode" can be segmented as "leet code".

UPDATE (2017/1/4):
The wordDict parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.

给定一个字符串和字典,确定字符串是否可以是字典中单词组成的。
思路:参考http://blog.csdn.net/linhuanmars/article/details/22358863/

原题目的字典是set集合,这里把vector字典转换成set集合,空间复杂度增大。这里需要注意的是循环的下标和字符串的个数之间的关系。

class Solution {
public:
    bool wordBreak(string s, vector& wordDict) {
                int m=s.size();
        int n=wordDict.size();
        if(m==0||n==0) return false;
        vector result(m+1,false);//这里字符串bool保存从0个字符串到m个字符串,这样的处理方式不需要特殊处理开始的部分。
        unordered_set dic;
        for(int i=0;i=0;j--)
            {
                if(result[j]&&dic.find(s.substr(j,i-j))!=dic.end())
                {
                    result[i]=true;
                    break;
                }
            }
        }
        return result[m];
    }
};

300. Longest Increasing Subsequence

Given an unsorted array of integers, find the length of longest increasing subsequence.

For example,
Given [10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.

Your algorithm should run in O(n2) complexity.

Follow up: Could you improve it to O(n log n) time complexity?

Credits:


279. Perfect Squares

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.

For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.

参考解析:

https://discuss.leetcode.com/topic/24255/summary-of-4-different-solutions-bfs-dp-static-dp-and-mathematics

class Solution {
public:
    int numSquares(int n) {
        if(n<=0) return 0;
       static vector res={0};
        while(res.size()<=n)
        {
            int size=res.size();
            int least=std::numeric_limits::max();
            for(int j=1;j*j<=size;j++)
            {
                least=min(least,res[size-j*j]+1);
            }
            res.push_back(least);
        }
        return res[n];
    }
    
};

96. Unique Binary Search Trees

Given n, how many structurally unique BST's (binary search trees) that store values 1...n?

For example,
Given n = 3, there are a total of 5 unique BST's.

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3
自己的思路:
当节点数为n时,在1到n中任选一个做头结点,则组合数是左右子树组合的乘积,因此公式是:G(n) = G(0) * G(n-1) + G(1) * G(n-2) + … + G(n-1) * G(0)

class Solution {
public:
    int numTrees(int n) {
     if(!n) return 0;
     vector res(n+1,0);
        res[0]=1;
        res[1]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<=i;j++)
                res[i]+=res[i-j]*res[j-1];
        return res[n];
    }
};
95. Unique Binary Search Trees II

Given an integer n, generate all structurally unique BST's (binary search trees) that store values 1...n.

For example,
Given n = 3, your program should return all 5 unique BST's shown below.

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3
这道题在上一题的基础上,要求生成所有的树
自己的思路:
首先选择一个数做根节点,然后根据之前生成的树,处理左右子树。因为之前生成的树最小值都是1,因此知道新的子树的最小值x之后,只要将模版树的每个节点值加上x-1即可。

下面是没有编完的代码:
class Solution {
public:
    vector generateTrees(int n) {
        vector> res(n+1);
        res[0].emplace(nullptr);
        res[1].emplace(TreeNode(1));
        for(int i=2;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                int left=j-1,right=i-j;
                for(int lefti=0;leftileft= generateTrees(res,i,j-1,1,j,true);
                        head->right= generateTrees(res,i,i-j,j+1,j,false);
                    } 
                }
                TreeNode* head=new TreeNode(j);
               head->left= generateTrees(res,i,j-1,1,j,true);
                head->right= generateTrees(res,i,i-j,j+1,j,false);
                res[i].push_back(head);
               // res[i]+=res[i-j]*res[j-1];
            }
        }
        return res[n];
    }
    TreeNode* generateTrees(vector> &res,int i,int num,int minimal,int j,bool isleft){
        //结果,当前行,子树节点数,节点中的最小值,头结点,是否是左子树
        if(num==0) {
            return nullptr;
        };
        int n=res[num].size();
        for(int k=0;kleft=copyTrees(res,num,k,minimal-1);
            else
                head->right=copyTrees(res,num,k,minimal-1);
            res[i].push_back(head);
        } 
    }
    TreeNode* copyTrees(vector> &res,int num,int k,int minimal){
        if(num==0) return nullptr;
        TreeNode* head=res[num][k];
        return copyTrees(head,minimal);
    }
    TreeNode* copyTrees(TreeNode* head,int minimal){
        if(head==nullptr) return nullptr;
        TreeNode* newHead=new TreeNode(head->val+minimal);
        if(head->left) newHead->left=copyTrees(head->left,minimal);
        if(head->right) newHead->right=copyTrees(head->right,minimal);
        return newHead;
    }
};


322. Coin Change 换钱的最小货币数

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:
coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)

Example 2:
coins = [2], amount = 3
return -1.

Note:
You may assume that you have an infinite number of each kind of coin.






你可能感兴趣的:(动态规划)