动态规划题目汇总

1、最短编辑距离:

#include <iostream>
#include <string.h>
#include <vector>
#include <algorithm>//min()包含头文件 sf
using namespace std;
int main(){
	char str1[1025],str2[1025];//d//长度不超过1024,长度最小要声明为1024+1,因为字符串末尾有空字符。
	int n,m,temp;
	
	cout<<"hhhhh llll"<<endl;

	while(cin>>str1>>str2){//循环输入两个字符串
		m=strlen(str1);
		n=strlen(str2);

		cout<<"长度1:"<<m<<endl;
		cout<<"长度2:"<<n<<endl;


		vector< vector<int> > dp(m+1,vector<int>(n+1,0));//生成一个m+1行n+1列的二维矩阵记录当前的状态值
		//初始化
		for(int i=1;i<=m;i++)//dp[i][0]=i,例如dp[2][0]表示一个长度为2的字符串str1与一个空字符串str2的最小编辑距离为2(即依次将str1中的字符添加到str2中)
			dp[i][0]=i;
		for(int j=0;j<=n;j++)//dp[0][j]=j,例如dp[0][1]表示一个空字符串str1与一个长度为1的字符串str2的最小编辑距离为1(即依次将str2中的字符添加到str1中)
			dp[0][j]=j;
		dp[0][0]=0;//空字符串与空字符串之间的最小编辑距离为0
		for(int i=1;i<=m;i++){
			for(int j=1;j<=n;j++){
				if(str2[j-1]==str1[i-1])//注意:字符串str1和str2中的索引是从0开始的,而1<=i<=m,1<=j<=n,所以这里的i和j要减1
					dp[i][j]=dp[i-1][j-1];
				else{
					temp=min(dp[i][j-1],dp[i-1][j]);
					dp[i][j]=min(temp,dp[i-1][j-1])+1;
				}	
			}
		}
		cout<<dp[m][n]<<endl;//最终的dp[m][n]为两字符串之间的最小编辑距离
	}
	return 0;
}

  

2、背包问题:

描述

在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]

你不可以将物品进行切割。

样例

样例 1:
	输入:  [3,4,8,5], backpack size=10
	输出:  9

样例 2:
	输入:  [2,3,5,7], backpack size=12
	输出:  12
	

 思路:

dp[k][n] 的定义如下:对于前 k 个物品,当前背包的容量为 n ,这种情况下可以装的最大价值是dp[k][n]。

解题框架:

int dp[N+1][W+1]
dp[0][..] = 0
dp[..][0] = 0
for i in [1..N]:
    for w in [1..W]:
        dp[i][w] = max( 把物品 i 装进背包,不把物品 i 装进背包 )

return dp[N][W]

 

 
class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @return: The maximum size
     */
    int backPack(int m, vector<int> &A) {
        // write your code here
        int len = A.size()+1;
        m = m+1;
  
        vector<vector<int>> dp(len, vector<int> (m, 0));
        // int dp[len][m];
        // vector<vector<int>> dp[len][m];
        // for (int i=0;i<len;i++){
        //     dp[i][0] = 0;
        // }
        // for (int i= 0;i<m;i++){
        //     dp[0][i] = 0;
        // }
        
        for(int k =1;k<len;k++){
            for(int n=1;n<m;n++){
                if(n-A[k-1]<0){
                    dp[k][n]=dp[k-1][n];
                }
                else{
                    dp[k][n]= max(dp[k-1][n],dp[k-1][n-A[k-1]]+A[k-1]);
                }
            }
        }
        
        return dp[len-1][m-1];
    }
};

  

背包问题2

描述

 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V表示每个物品的价值.

问最多能装入背包的总价值是多大?

  1. A[i], V[i], n, m 均为整数
  2. 你不能将物品进行切分
  3. 你所挑选的要装入背包的物品的总大小不能超过 m
  4. 每个物品只能取一次

样例 1:

输入: m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]
输出: 9
解释: 装入 A[1] 和 A[3] 可以得到最大价值, V[1] + V[3] = 9 

样例 2:

输入: m = 10, A = [2, 3, 8], V = [2, 5, 8]
输出: 10
解释: 装入 A[0] 和 A[2] 可以得到最大价值, V[0] + V[2] = 10

思路:和0-1背包问题的区别只在于当物品可以放入时,dp[k][n]= max(dp[k-1][n],dp[k-1][n-A[k-1]]+V[k-1]);
这里时减去体积,加上价值;上面的问题价值和体积是一样的数值;
 
class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @param V: Given n items with value V[i]
     * @return: The maximum value
     */
    int backPackII(int m, vector<int> &A, vector<int> &V) {
        // write your code here
        int len = A.size()+1;
        m = m+1;
  
        vector<vector<int>> dp(len, vector<int> (m, 0));
        // int dp[len][m];
        // vector<vector<int>> dp[len][m];
        // for (int i=0;i<len;i++){
        //     dp[i][0] = 0;
        // }
        // for (int i= 0;i<m;i++){
        //     dp[0][i] = 0;
        // }
        
        for(int k =1;k<len;k++){
            for(int n=1;n<m;n++){
                if(n-A[k-1]<0){
                    dp[k][n]=dp[k-1][n];
                }
                else{
                    dp[k][n]= max(dp[k-1][n],dp[k-1][n-A[k-1]]+V[k-1]);
                }
            }
        }
        
        return dp[len-1][m-1];
    }
};

  

3、连续最大和

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

示例:

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

思路:动态数组dp[i]表示,以第i个元素最为结尾的子数组,连续最大和是多少;

对每个元素i,都有两种选择,加入子数组或者单独作为子数组的开始元素;

所以:dp[i] = max(nums[i],nums[i]+dp_1)

然后遍历dp数组取最大值即可;

或者采用一个遍历随时记录最大值,可以节省空间;

-----------------------------------------

事实上,当我们令curSum为当前最大子数组的和,maxSum为最后要返回的最大子数组的和

  1. (继承前人遗产吗) 当我们往后扫描时,对第j+1个元素有两种选择——要么放入前面找到的子数组,要么做为新子数组的第一个元素:
    1.1 如果currSum+当前元素a[j] >= a[j],则令currSum加上a[j]
    1.2 否则currSum重新赋值,置为下一个元素,即currSum = a[j]。
  2. (更新历代最强吗) 比较当前最大子数组和与最大子数组的和:
    2.1 同时,当currSum > maxSum,则更新maxSum = currSum
    2.2 否则保持原值,不更新。


curSum = max(a[j], curSum + a[j])
maxSum = max(maxSum, curSum)

举个例子,当输入数组是1, -2, 3, 10, -4, 7, 2, -5,那么,curSum和maxSum相应的变化为:
curSum : 0 | 1 -1 3 13 9 16 18 13
maxSum : 1 | 1 1 3 13 13 16 18 18

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int m = size(nums);
        if (m == 0) return 0;
        int dp_1= nums[0];
        int dp_2= 0;
        int res = dp_1;
        for (int i=1; i< m ; i++){
            dp_2 = max(nums[i],nums[i]+dp_1);
            dp_1 = dp_2;
            res = max(res,dp_2);

        }
        return res;
    }
};

  

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int m = size(nums);
        vector<int> dp(m);
        dp[0] = nums[0];

        for(int i=1;i<m;i++){
            dp[i]=max(nums[i],nums[i]+dp[i-1]);
        }

        int res=nums[0];
        for(auto n:dp){
            if(res<n){
                res=n;
            }
        }
        return res;
    }
};

  

4、最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

思路:与上题类似,dp[i]表示以nums[i]结尾的数组的最长递增子序列长度

当前元素大于前面的元素时

dp[i] = max(dp[j] +1 ) 此时,0<j<i 

-----------------------------------------

比如,处理最后一个元素 5,发现比前面的元素都大,那么以 5 结尾的 LIS 的长度可能为

dp[0] + 1,dp[1] + 1,dp[2] + 1,dp[3] + 1。

其中的最大值为 dp[2] + 1 = 3,因此 LIS 的长度为 3。

总结:

dp[i] 默认都为 1,因为以 i 结尾的 LIS 至少包含自己。

在 dp 表 0~i-1 中比对时,若 arr[i] > arr[j],

那么 dp[j] + 1 可能为 dp[i] 的最终值。

需要在所有的可能值中取最大值。

时间复杂度为 O(n2)

https://blog.csdn.net/qq_41765114/article/details/88415541

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {

        int len = nums.size();
        vector<int > dp(len,0);
        if (len == 0){
            return 0;
        }
        if (len == 1){
            dp[0]= 1;
            return 1;
        }

        dp[0]=1;
        int res2 =1;
        for(int i=1;i<len;i++){
            int res =1;
            for(int j=0;j<=i;j++){
                if(nums[i]>nums[j]){
                    res = max(res,dp[j]+1);
                }
            }
            dp[i] = res;
            res2 = max(res,res2);
        }
        return res2;
    
    }
};

  

最长上升子序列:

思路:dp[i]表示以nums[i]结尾的数组的最长递增子序列长度

当前元素大于前面的元素时:
dp[i] = max(dp[j] +1 ) 此时,0<j<i 

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = nums.size();
        if(len==0){
            return 0;
        }
        if(len==1){
            return 1;
        }

        vector<int> dp(len);
        dp[0]=1;

        int res=1;

        for(int i=1;i<len;i++){
            int temp=1;
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    temp=max(temp,dp[j]+1);
                }
            }
            dp[i]=temp;
            res = max(res,dp[i]);
        }
        return res;
    }
};

  

5、乘积最大子数组

给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

示例 1:

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

思路:与连续最大和思路类似,但这里需要注意乘积计算遇到负数的情况

动态数组dp[i]表示,以第i个元素最为结尾的子数组,连续最大乘积是多少;

遍历数组时计算当前最大值,不断更新;


令imax为当前最大值,则当前最大值为 imax = max(imax * nums[i], nums[i])
由于存在负数,那么会导致最大的变最小的,最小的变最大的。因此还需要维护当前最小值imin,
imin = min(imin * nums[i], nums[i])
当负数出现时则imax与imin进行交换再进行下一步计算
时间复杂度:O(n)

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int len= nums.size();
        if (len ==1){
            return nums[0];
        }
        vector<int> dp(len,0);
        dp[0] = nums[0];
        int res = dp[0];
        int imax = dp[0];
        int imin = dp[0];        

        for (int i=1;i<len;i++){
            if(nums[i]<0){
                int temp = imax;
                imax = imin;
                imin = temp;
            }
            imax = max(imax*nums[i],nums[i]);
            imin = min(imin*nums[i],nums[i]);

            res = max(res,imax);
        }

        return res;
    }
};

  

6、最小路径和:

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例:

输入:
[
  [1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

思路 : 应该用动态规划思路(贪心?)
dp[i][j]表示到i,j表示的单元格,最小和
dp[i][j] 的值只和上面的数和左边的数有关系,取两种路径中最小的即可
第一行,第一列特殊处理,然后,根据给的样例,写出递推矩阵

1,4,5
2,7,6
6,8,7

一般化:dp[i][j] = min(dp[i-1][j],dp[i][j-1])+grid[i][j];

遇到这种问题,可以动动手,大致演算一下,找到其中的状态转移方程,本题一次可以AC

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
    	int row = grid.size();
    	int col = grid[0].size();

    	vector<vector<int>> dp(row,vector<int>(col,0));

    	int sum = 0;
    	for(int i=0;i<row;i++){
    		dp[i][0]=sum+grid[i][0];
    		sum = sum+grid[i][0];
    	}

    	sum = 0;
    	for(int j=0;j<col;j++){
    		dp[0][j]=sum+grid[0][j];
    		sum = sum+grid[0][j];
    	}

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

    	return dp[row-1][col-1];

    }
};

  

7、零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1
示例 2:

输入: coins = [2], amount = 3
输出: -1
 

说明:
你可以认为每种硬币的数量是无限的。

思路(动态规划):一般动动手,把例子中的结果递推出来,然后抽象成状态转移方程,再写代码:

动态规划解题套路框架:
1)先确定「状态」,也就是原问题和子问题中变化的变量。由于硬币数量无 限,所以唯一的状态就是目标金额 amount 。
2)然后确定 dp 函数的定义:当前的目标金额是 n ,至少需要 dp(n) 个硬 币凑出该金额。
3)然后确定「选择」并择优,也就是对于每个状态,可以做出什么选择改变当 前状态。具体到这个问题,无论当的目标金额是多少,选择就是从面额列表
coins 中选择一个硬币,然后目标金额就会减少:

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {

    	// 初始化动态数组,为一个比较大的初始值
    	vector<int> dp(amount+1,amount+999);

    	if(amount == 0){
    		return 0;
    	}

    	if(amount <0){
    		return -1;
    	}

    	dp[0]=0;
    	//i代表要凑出的金额
    	for(int i=0;i<dp.size();i++){
    		for(int coin:coins){
                // 判断能否加入
    			if(i-coin<0) continue;
                // 状态变化情况
    			dp[i]=min(dp[i],dp[i-coin]+1);
    		}
    	}

    	if(dp[amount]==amount+999){
    		return -1;
    	}

    	return dp[amount];

    }
};

  

零钱兑换II:
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

思路:
我们可以把这个问题转化为背包问题的描述形式:
int change(int amount, int[] coins);
经典动态规划:完全背包问题
有一个背包,最大容量为 amount ,有一系列物品 coins ,每个物品的重 量为 coins[i] ,每个物品的数量无限。请问有多少种方法,能够把背包恰 好装满?
这个问题和我们前面讲过的两个背包问题,有一个最大的区别就是,每个物 品的数量是无限的,这也就是传说中的「完全背包问题」,没啥高大上的, 无非就是状态转移方程有一点变化而已。
下面就以背包问题的描述形式,继续按照流程来分析。
解题思路 第一步要明确两点,「状态」和「选择」。
状态有两个,就是「背包的容量」和「可选择的物品」,选择就是「装进背
包」或者「不装进背包」嘛,背包问题的套路都是这样。
明白了状态和选择,动态规划问题基本上就解决了,只要往这个框架套就完
事儿了:
第二步要明确 dp 数组的定义。 首先看看刚才找到的「状态」,有两个,也就是说我们需要一个二维 dp
数组。
dp[i][j] 的定义如下:
若只使用前 i 个物品,当背包容量为 j 时,有 dp[i][j] 种方法可以装 满背包。
换句话说,翻译回我们题目的意思就是:
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态1][状态2][...] = 计算(选择1,选择2...)
152

经典动态规划:完全背包问题
若只使用 coins 中的前 i 个硬币的面值,若想凑出金额 j ,有 dp[i] [j] 种凑法。
经过以上的定义,可以得到:
basecase为 dp[0][..] = 0, dp[..][0] = 1 。因为如果不使用任何硬币面 值,就无法凑出任何金额;如果凑出的目标金额为 0,那么“无为而治”就是 唯一的一种凑法。
我们最终想得到的答案就是 dp[N][amount] ,其中 N 为 coins 数组的大 小。
大致的伪码思路如下:
int dp[N+1][amount+1] dp[0][..] = 0 dp[..][0] = 1
for i in [1..N]:
for j in [1..amount]:
把物品 i 装进背包,
不把物品 i 装进背包 return dp[N][amount]
第三步,根据「选择」,思考状态转移的逻辑。 注意,我们这个问题的特殊点在于物品的数量是无限的,所以这里和之前写
的背包问题文章有所不同。
如果你不把这第 i 个物品装入背包,也就是说你不使用 coins[i] 这个面 值的硬币,那么凑出面额 j 的方法数 dp[i][j] 应该等于 dp[i-1][j] , 继承之前的结果。
如果你把这第 i 个物品装入了背包,也就是说你使用 coins[i] 这个面值 的硬币,那么 dp[i][j] 应该等于 dp[i][j-coins[i-1]] 。
首先由于 i 是从 1 开始的,所以 coins 的索引是 i-1 时表示第 i 个硬 币的面值。

经典动态规划:完全背包问题
dp[i][j-coins[i-1]] 也不难理解,如果你决定使用这个面值的硬币,那么 就应该关注如何凑出金额 j - coins[i-1] 。
比如说,你想用面值为 2 的硬币凑出金额 5,那么如果你知道了凑出金额 3 的方法,再加上一枚面额为 2 的硬币,不就可以凑出 5 了嘛。
综上就是两种选择,而我们想求的 dp[i][j] 是「共有多少种凑法」,所以 dp[i][j] 的值应该是以上两种选择的结果之和:

class Solution {
public:
    int change(int amount, vector<int>& coins) {

    	int len = coins.size();
    	vector<vector<int>> dp(len+1,vector<int>(amount+1));

    	// 初始化状态
    	for (int i = 0; i <= len; i++){
    		dp[i][0] = 1;
    	}

    	for (int i = 1; i <= len; i++) {
    		for (int j = 1; j <= amount; j++){
    			if (j - coins[i-1] >= 0){
    				dp[i][j] = dp[i - 1][j]+ dp[i][j - coins[i-1]]; 
    			}else{
    				dp[i][j] = dp[i - 1][j];
    			}
    		}
    	}

    	return dp[len][amount];

    }
};

  

而且,我们通过观察可以发现, dp 数组的转移只和 dp[i][..] 和 dp[i- 1][..] 有关,所以可以压缩状态,进一步降低算法的空间复杂度:
这个解法和之前的思路完全相同,将二维 dp 数组压缩为一维,时间复杂 度 O(N*amount),空间复杂度 O(amount)。

解法一中,会造成大量的重复计算,因此我们通过缓存结果来提高性能。通过解法一种,我们知道,整个组合数就是:

  • 对每种硬币类型只有选和不选两种结果
  • 选择后 amount = amount -(选择的硬币面额)
class Solution {
public:
    int change(int amount, vector<int>& coins) {

    	int len = coins.size();

    	vector<int> dp(amount+1);

    	dp[0] = 1;

    	for(int i=0;i<len;i++){
    		for(int j=1;j<=amount;j++){
    			if(j-coins[i]>=0){
    				dp[j]=dp[j]+dp[j-coins[i]];
    			}
    		}
    	}

    	return dp[amount];


    }
};

  

121. 买卖股票的最佳时机
给定一个数组,它的第 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。


思路:
1)常规思路,元素两两做差求最大值,差小于0则置为0,复杂度为o(n*n)
2)动态规划思路,dp[i]表示在第i天卖出的最大收益,就等于第i-1天不卖的最大收益,加上第i天-第i-1天的价格差
dp[i]=max(0,dp[i-1]+prices[i]-prices[i-1]) , 复杂度为o(n)

class Solution {
public:
    int maxProfit(vector<int>& prices) {

        int len = prices.size();
        vector<int> dp(len+1,0);

        dp[0]=0;

        for(int i=1;i<len; i++){
            dp[i]=max(0,dp[i-1]+prices[i]-prices[i-1]);
        }
        int max = 0;
        for(auto x:dp){
            if(x>max){
                max = x;
            }
        }

        return max;

    }
};

  

62、不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?


思路:这个动态规划题目很简单:

* * *
* * *
* * *

左上角到右下角:
1 1 1
1 2 3
1 3 6


根据输入矩阵大小,例举出不同路径的个数,得到状态转移方程
dp[i][j]=dp[i][j-1]+dp[i-1][j]
解释为:到[i,j]的路径数为,到上面的点[i-1][j]的路径数,加上到左边的点[i][j-1]的路径数,加上到左边的点

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m,vector<int>(n,1));
        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];
            }
        }

        return dp[m-1][n-1];
    }
};

  

70、爬楼梯:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶


4
1--1--1--1
1--2--1
1--1--2
2--1--1
2--2


思路:也很简单,爬到第n阶的路径为爬到第n-1阶级的路径加上爬到第n-2阶的路径个数

所以状态转移方程:
dp[n]=dp[n-1]+dp[n-2]

dp[1]=1
dp[2]=2

class Solution {
public:
    int climbStairs(int n) {
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }

        vector<int> dp(n);
        dp[0]=1;
        dp[1]=2;

        for(int i=2;i<n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }

        return dp[n-1];

    }
};

  

18. 杨辉三角
给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。

思路:按照输出,写递推方程即可,基本一次AC
考虑输入行数为,0,1,2的特殊情况即可


[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        if(numRows==0){
            return {};
        }

        if(numRows==1){
            return {{1}};
        }

        if(numRows==2){
            return {{1},{1,1}};
        }

        vector<vector<int>> res;
        vector<int> vec1={1};
        vector<int> vec2={1,1};

        res.push_back(vec1);
        res.push_back(vec2);

        for(int i=2;i<numRows;i++){
            vector<int> temp;
            temp.push_back(1);
            for(int j=1;j<i;j++){
                temp.push_back(res[i-1][j-1]+res[i-1][j]);
            }
            temp.push_back(1);
            res.push_back(temp);
        }
        return res;
    }
};

  

1277. 统计全为 1 的正方形子矩阵
给你一个 m * n 的矩阵,矩阵中的元素不是 0 就是 1,请你统计并返回其中完全由 1 组成的 正方形 子矩阵的个数。

示例 1:

输入:matrix =
[
[0,1,1,1],
[1,1,1,1],
[0,1,1,1]
]

0 1 1 1
1 1 2 2
0 1 2 3

求和后,为15

输出:15
解释:
边长为 1 的正方形有 10 个。
边长为 2 的正方形有 4 个。
边长为 3 的正方形有 1 个。
正方形的总数 = 10 + 4 + 1 = 15.
示例 2:

输入:matrix =
[
[1,0,1],
[1,1,0],
[1,1,0]
]

1 0 1
1 1 0
1 2 0

求和后为7


输出:7
解释:
边长为 1 的正方形有 6 个。
边长为 2 的正方形有 1 个。
正方形的总数 = 6 + 1 = 7.

思路:还是动态规划思路,通过列举结果矩阵,找到一般化的规律。
在这里,创一个dp数组等于matrix,每个点比较其左边、上边和左上三个点的关系, 动态方程为:
dp[i][j]=min(dp[i-1][j],dp[i-1][j-1],dp[i][j-1])+1
dp[i][j]定义为,以i,j 为右下角的正方形的数目;
所以,在计算出所有的 dp[i][j] 后,我们将它们进行累加,就可以得到矩阵中正方形的数目。

https://leetcode-cn.com/problems/count-square-submatrices-with-all-ones/solution/tong-ji-quan-wei-1-de-zheng-fang-xing-zi-ju-zhen-2/

class Solution {
public:
    int countSquares(vector<vector<int>>& matrix) {
        if(matrix.size()==0) return 0;
        int m = matrix.size();
        int n = matrix[0].size();
        int ans = 0;
        vector<vector<int>> dp(m, vector<int>(n, 0));


        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(matrix[i][j])
                {
                    if(i==0 || j==0){
                        dp[i][j] = 1;
                    }
                    else{
                        dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
                    }
                    ans += dp[i][j];
                }
            }
        }
        return ans;
    }
};

  

原文地址:https://www.cnblogs.com/Allen-rg/p/13679827.html