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)




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;
}
};