动态规划题目汇总
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
表示每个物品的价值.
问最多能装入背包的总价值是多大?
A[i], V[i], n, m
均为整数- 你不能将物品进行切分
- 你所挑选的要装入背包的物品的总大小不能超过
m
- 每个物品只能取一次
样例 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为最后要返回的最大子数组的和
- (继承前人遗产吗) 当我们往后扫描时,
对第j+1个元素有两种选择
——要么放入前面找到的子数组,要么做为新子数组的第一个元素:
1.1 如果currSum+当前元素a[j] >= a[j]
,则令currSum加上a[j]
1.2 否则currSum重新赋值,置为下一个元素,即currSum = a[j]。 - (更新历代最强吗) 比较当前最大子数组和与最大子数组的和:
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; } };