【Leetcode 动态规划】 不知如何分类 就都放这里了

338. Counting Bits

Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1‘s in their binary representation and
return them as an array.

Example:

For num = 5 you should return [0,1,1,2,1,2].

意思是给你一个非负整数num,对于 0~num 这(num+1)个整数,求出每个数用二进制表示时1的个数。

最简单的思路:对每个数,利用移位和按位与(i & 1)运算,计算1的个数。这样时间复杂度为O(n*sizeof(integer)),如果int用32位表示,那么时间复杂度就是O(32n)。

考虑优化成O(n):

对于11这个数,我们暂时用一个字节来表示

11:           0000 1011

11/2 = 5: 0000 0101

容易发现,除了11最右边那个位和5的最高位,其他位对应一样。也就是说i用二进制表示时1出现的次数等于i/2中1出现的次数加1(如果i用二进制表示时最右边一位为1,否则不加1)。这样我们在计算i时可以利用前面已计算出的i/2:ret[i] = ret[i/2] + (i % 2 == 0 ? 0 : 1)  即 ret[i] = ret[i/2] + i % 2。

vector<int> countBits(int num) {
    vector<int> ret(num + 1, 0);
    for(int i = 1; i <= num; ++i)
        ret[i] = ret[i>>1] + i % 2;
    return ret;
}

343. Integer Break

Given a positive integer n, break it into the sum of at least two positive integers and maximize the product of those integers. Return the maximum product you can get.

For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).

Note: You may assume that n is not less than 2 and not larger than 58.

Hint:

  1. There is a simple O(n) solution to this problem.
  2. You may check the breaking results of n ranging from 7 to 10 to discover the regularities.

这道题给了我们一个正整数n,让我们拆分成至少两个正整数之和,使其乘积最大,题目提示中让我们用O(n)来解题,而且告诉我们找7到10之间的规律,那么我们一点一点的来分析:

从1开始,但是1不能拆分成两个正整数之和,所以不能当输出。

2只能拆成1+1,所以乘积也为1。

数字3可以拆分成2+1或1+1+1,显然第一种拆分方法乘积大为2。

数字4拆成2+2,乘积最大,为4。

数字5拆成3+2,乘积最大,为6。

数字6拆成3+3,乘积最大,为9。

数字7拆为3+4,乘积最大,为12。

数字8拆为3+3+2,乘积最大,为18。

数字9拆为3+3+3,乘积最大,为27。

数字10拆为3+3+4,乘积最大,为36。

....

那么通过观察上面的规律,我们可以看出从5开始,数字都需要先拆出所有的3,一直拆到剩下一个数为2或者4,因为剩4就不用再拆了,拆成两个2和不拆没有意义,而且4不能拆出一个3剩一个1,这样会比拆成2+2的乘积小。那么这样我们就可以写代码了,先预处理n为2和3的情况,然后先将结果res初始化为1,然后当n大于4开始循环,我们结果自乘3,n自减3,根据之前的分析,当跳出循环时,n只能是2或者4,再乘以res返回即可:

int integerBreak(int n) {
    if (n == 2 || n == 3)
		return n - 1;
    int res = 1;
    while (n > 4) {
        res *= 3;
        n -= 3;
    }
    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 == 0时,结果为0;

如果n == 1时,只有一个节点,结果为1;

如果n == 2时,根节点有两种选择,结果为2;

如果n >= 3时,n个点中每个点都可以作为root,当 i 作为root时,小于 i  的点都只能放在其左子树中,大于 i 的点只能放在右子树中,此时只需求出左、右子树各有多少种,二者相乘即为以 i 作为root时BST的总数。

class Solution {
public:
    int numTrees(int n) {
        if(n <= 2)
            return n;
        vector<int> dp(n + 1, 0);
        dp[0] = 1;
        dp[1] = 1;
        dp[2] = 2;

        for(int i = 3; i <= n; ++i) {
            int tmp = 0;
            for(int j = 0; j < i; ++j) {
                tmp += dp[j] * dp[ i - j - 1];
            }
            dp[i] = tmp;
        }
        return dp[n];
    }
};

62. Unique Paths

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?

一、题目描述:

给定一个m*n的矩阵,让机器人从左上方走到右下方,只能往下和往右走,一共多少种走法。

二、解题方法:

//动态规划:
//设状态为f[i][j],表示从起点(1;1)到达(i; j)的路线条数,则状态转移方程为:
//f[i][j] = f[i-1][j] + f[i][j-1]
class Solution {
public:
	int uniquePaths(int m, int n) {
		vector<vector<int> > f(m, vector<int>(n, 1));
		for (int i = 1; i < m; i++)
			for (int j = 1; j < n; j++)
				f[i][j] = f[i - 1][j] + f[i][j - 1];
		return f[m - 1][n - 1];
	}
};

上面方法的空间复杂度较大为O(m*n),然而通过观察可以发现,我们每次更新f[i][j]只需要f[i-1][j](同一列)和f[i][j-1](左一列),所以只要保存当前列和左一列就行,而不是整个m*n矩阵,下面的代码可以将空间复杂度优化到O(min(m,n))

class Solution {
	int uniquePaths(int m, int n) {
		if (m > n) return uniquePaths(n, m);
		vector<int> pre(m, 1);
		vector<int> cur(m, 1);
		for (int j = 1; j < n; j++) {
			for (int i = 1; i < m; i++)
				cur[i] = cur[i - 1] + pre[i];
			swap(pre, cur);
		}
		return pre[m - 1];
	}
};

通过进一步的观察,我们还可以发现,上面程序中的pre[i]就是更新前的cur[i],所以可以进一步优化为:

class Solution {
    int uniquePaths(int m, int n) {
        if (m > n) return uniquePaths(n, m);
        vector<int> cur(m, 1);
        for (int j = 1; j < n; j++)
            for (int i = 1; i < m; i++)
                cur[i] += cur[i - 1];
        return cur[m - 1];
    }
}; 

最终优化空间程序为:(其实先遍历m还是先遍历n都无所谓的。关键是要注意 vector的长度 与 内层for循环的长度 是一样的~!)

class Solution{
public:
	int uniquePaths(int m, int n) {
		if (m == 0 && n == 0)
			return 0;

		vector<int> dp(n, 1);
		for (int i = 1; i < m; i++)
			for (int j = 1; j < n; j++)
				dp[j] = dp[j - 1] + dp[j];

		return dp[n - 1];
	}
};

63. Unique Paths II

Follow up for "Unique Paths":

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and empty space is marked as 1 and 0 respectively
in the grid.

For example,

There is one obstacle in the middle of a 3x3 grid as illustrated below.

[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]

The total number of unique paths is 2.

Note: m and n will be at most 100.

这道题跟 Unique Paths 差不多,只是这道题给机器人加了障碍,不是每次都有两个选择(向右,向下)了。

因为有了这个条件,所以 Unique Paths 中最后一个直接求组合的方法就不适用了,这里最好的解法就是用动态规划了。

递推式还是跟 Unique Paths 一样,只是每次我们要判断一下是不是障碍,如果是障碍,则dp[i][j] = 0;

否则还是dp[i][j] = dp[i - 1][j] + dp[i][j - 1]。

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        if(m == 0 || n ==0)
            return 0;

        vector<int> dp(n);
        dp[0] = 1;
        for(int i = 0; i < m; ++i) {
            for(int j = 0; j < n; ++j) {
                if(obstacleGrid[i][j] == 1)
                    dp[j] = 0;
                else if(j > 0)
                    dp[j] += dp[j - 1];
            }
        }
        return dp[n - 1];
    }
};

64. Minimum Path Sum

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.

这是动态规划的问题,由于每次只能向下或者向右移动,因此[i, j]位置时的最小路径的和等于[i, j-1] 与 [i-1, j]中较小的加上[i, j]位置的数值。

因此递推公式是grid[i][j] += min(grid[i][j-1],  grid[i-1][j])。

时间复杂度:O(mn)

空间复杂度:O(mn)

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

        vector<vector<int> > dp (m, vector<int>(n, 0));
        dp[0][0] = grid[0][0];

        for(int i = 1; i < m; ++i)
            dp[i][0] += grid[i][0] + dp[i - 1][0];
        for(int i = 1; i < n; ++i)
            dp[0][i] += grid[0][i] + dp[0][i - 1];

        for(int i = 1; i < m; ++i) {
            for(int j = 1; j < n; ++j) {
                dp[i][j] += grid[i][j] + min(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        return dp[m - 1][n - 1];
    }
};

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
.

这道题说是给我们一个正整数,求它最少能由几个完全平方数组成。

用动态规划Dynamic Programming来做,我们建立一个长度为n+1的一维dp数组,将第一个值初始化为0,其余值都初始化为INT_MAX.i从0循环到n,j从1循环到i+j*j <= n的位置,然后每次更新dp[i+j*j]的值,动态更新dp数组,其中dp[i]表示正整数i能少能由多个完全平方数组成,那么我们求n,就是返回dp[n]即可,也就是dp数组的最后一个数字,参见代码如下:

class Solution {
public:
    int numSquares(int n) {

        vector<int> dp(n + 1, 0x7fffffff);
        for(int i = 0; i * i <= n; ++i)
            dp[i * i] = 1;

        for(int i = 1; i <= n; ++i) {
            for(int j = 1; i + j * j <= n; ++j) {
                dp[i + j * j] = min(dp[i] + 1, dp[i + j * j]);
            }
        }
        return dp[n];
    }
};

120. Triangle

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).

Note:

Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

分析:

求一个三角形二维数组从顶到低端的最小路径和。每次只能挪一个位置。

我们从低端向顶端计算。设状态为 S[i][j]表示从从位置 ( i, j ) 出发,到最低端路径的最小和

状态转移方程:S[i][j] = min(S[i+1][j] + S[i+1][j+1]) +S[i][j]

S[0][0]就是要求解的答案。

时间复杂度 O(n^2) ,空间复杂度 O(1)

class Solution {
public:
    int minimumTotal(vector<vector<int> > &triangle) {
        int size = triangle.size();
        // down-to-top
        // 第i层
        for(int i = size - 2;i >= 0;--i){
            // 第i层的第j个元素
            for(int j = 0;j <= i;++j){
                triangle[i][j] += min(triangle[i+1][j], triangle[i+1][j+1]);
            }
        }
        return triangle[0][0];
    }
};

从上面思路的状态转移方程中看出:S[i][j] = min(S[i+1][j] + S[i+1][j+1]) +S[i][j]

S[i][j]只与下一行的第j个元素和第j+1个元素相关,i的关系是固定的,因此我们可以省去这一维。

开辟O(N)的数组,然后规划的时候使用S[j] = min(S[j+1], S[j) +Triangle[i][j]就可以了。

class Solution {
    public:
    int minimumTotal(vector<vector<int> > &triangle) {
        int n = triangle.size();
        vector<int> dp(triangle.back());//dp初值设为triangle的最后一行
        // down-to-top
        // 第i层
        for(int i = n - 2;i >= 0;--i){
            // 第i层的第j个元素
            for(int j = 0;j <= i;++j){
                dp[j] = min(dp[j], dp[j+1]) + triangle[i][j];
            }
        }
        return dp[0];
    }
};
时间: 2024-10-12 04:59:48

【Leetcode 动态规划】 不知如何分类 就都放这里了的相关文章

leetcode 题型 数据结构 解法 分类总结

第2章 线性表 2.1 数组 2.1.1 Remove Duplicates from Sorted Array 2.1.2 Remove Duplicates from Sorted Array II 2.1.3 Search in Rotated Sorted Array 2.1.4 Search in Rotated Sorted Array II 2.1.5 Median of Two Sorted Arrays 2.1.6 Longest Consecutive Sequence 2.

Leetcode 动态规划 Decode Ways

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie Decode Ways Total Accepted: 8689 Total Submissions: 55465 A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' -> 1 'B' -> 2 ... 'Z' -> 26 Given a

[LeetCode] 动态规划入门题目

最近接触了动态规划这个厉害的方法,还在慢慢地试着去了解这种思想,因此就在LeetCode上面找了几道比较简单的题目练了练手. 首先,动态规划是什么呢?很多人认为把它称作一种"算法",其实我认为把它称作一种"思想"更为合适:利用动态规划去解决问题,其实就是逐步递推的过程,与贪心算法不同,动态规划递推的每一步都要求是当前的最优解(这是很重要的,递推的正确性依赖的就是这一点):利用动态规划解题时,必须自己定义出来状态和状态转移方程.然而,看上去简单,做起来却非常困难,因为

快速上手leetcode动态规划题

快速上手leetcode动态规划题 我现在是初学的状态,在此来记录我的刷题过程,便于以后复习巩固. 我leetcode从动态规划开始刷,语言用的java. 一.了解动态规划 我上网查了一下动态规划,了解到动态规划是“带有备忘录的递归”, 而大多数用来理解动态规划的例子都是斐波那契数列,就是那个经典的递归式 f(i)=f(i-1)+f(i-2) ,f(1)=f(2)=1 那么我们就可以得到很多式子,比如求f(5): f(5)=f(4)+f(3); f(4)=f(3)+f(2); f(3)=f(2)

Leetcode 动态规划 Maximum Subarray

正整数或一位小数或者俩位小数的正则表达式的写法 ^(?!0+(?:\.0+)?$)(?:[1-9]\d*|0)(?:\.\d{1,2})?$ Leetcode 动态规划 Maximum Subarray,布布扣,bubuko.com

Leetcode 动态规划 Climbing Stairs

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie Climbing Stairs Total Accepted: 13319 Total Submissions: 40778 You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb 1 or 2 steps. In how many distinc

Leetcode动态规划【简单题】

目录 Leetcode动态规划[简单题] 53. 最大子序和 题目描述 思路分析 复杂度分析 70.爬楼梯 题目描述 思路分析 复杂度分析 121.买卖股票的最佳时机 题目描述 思路分析 复杂度分析 303.区域和检索-数组不可变 题目描述 思路分析 复杂度分析 Leetcode动态规划[简单题] 动态规划(Dynamic programming,简称DP),是一种把原问题分解为相对简单的子问题的方式求解复杂问题的方法.动态规划相较于递归,拥有更少的计算量. 53. 最大子序和 题目描述 给定一

&lt;LeetCode&gt;动态规划——Two Sequences 类型题目

一.类型描述 Two Sequences 的题目一般会提供两个sequence,一般问最大/最小.true/false.count(*)这几类的问题. 其中,Two Sequences的动态规划题目的四要素: state:dp[i][j] 一般表示 第一个sequence的前i个字符 和 第二个sequence的前j个字符 怎么怎么样. Initialization: 这类型动态规划一般初始化dp数组的方式是根据题目的含义初始化第一行和第一列. function:解决动态规划的function问

LeetCode动态规划题总结【持续更新】

以下题号均为LeetCode题号,便于查看原题. 10. Regular Expression Matching 题意:实现字符串的正则匹配,包含'.' 和 '*'.'.' 匹配任意一个字符,"*" 匹配 '*' 之前的0个或多个字符. example: isMatch("aa","a") → false isMatch("aa","aa") → true isMatch("aaa",&