avatar

目录
动态规划合集

动态规划复杂度:
状态数量* 转移的计算量

  1. 三角形最小路径和
    行就是行,左斜的可以当做列。转移到dp[i][j]就有两种方式,dp[i-1][j]和dp[i-1][j-1]
c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int n=triangle.size();
int dp[n+1][n+1];
memset(dp,0x3f3f3f,sizeof(dp));
dp[1][1]=triangle[0][0];
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+triangle[i-1][j-1];
}
}
int res=0x3f3f3f;
for(int i=1;i<=n;i++){
res=min(res,dp[n][i]);
}
return res;
}
};
  1. 统计全1子矩形
    数的时候首先要思路清晰,划好界限不能遗漏,我们把每个右下角为(i,j)时有多少个计算好(设为f(i,j)),然后矩阵挨个位置遍历之后求和就可以了,计算f(i,j),我们首先把要知道每一列从上到下到(i,j)有多少个连续的1,这个需要先计算出来,比较好想,简单的dp就行了,看dp那部分.
    然后开始计算f(i,j)从右向左,宽度为1、2、3…分别有几种,比如图中就有12种,这个过程相当于遍历的过程记录最小值。
    复杂度on3o_{n^{3}}
    dp[i][j]表示以第i行j列的数往上(这一列)有多少个连续的1。
c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
int numSubmat(vector<vector<int>>& mat) {
int m=mat.size();
int n=mat[0].size();
int dp[m][n];
for(int i=0;i<n;i++){
dp[0][i]=mat[0][i];
}
for(int i=1;i<m;i++){
for(int j=0;j<n;j++){
if(mat[i][j]==1)
dp[i][j]=1+dp[i-1][j];
else
dp[i][j]=0;
}
}
int res=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
int mi=dp[i][j];
for(int k=j;k>=0;k--){
mi=min(mi,dp[i][k]);
res+=mi;
}
}
}
return res;
}
};

优化:单调栈(https://www.bilibili.com/video/BV115411Y7wV)

  1. 解码方式
    首先分析几个特殊的情况,之后开始dp
    dp[j]对应s[0]到s[i-1]的译码总数
    如果s[i]=0,前一位只能是1或2,dp[j]=dp[j-2];
    如果s[i-1]'1’或者s[i-1]‘2’&&s[i]<=‘6’,这两种情况后两位可以合并或者分开译码s[i-1]和s[i]分开译码就是dp[j-1],合并译码就是dp[j-2]
    其他情况就是只能分开译码了
c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
int numDecodings(string s) {
if(s.length()==0) return 0;
if(s[0]=='0')return 0;
if(s.length()==1) return 1;
int dp[s.size()+1];
dp[0]=dp[1]=1;
for(int i=1,j=2;i<s.size();i++,j++)
{
if(s[i]=='0')
{
if(s[i-1]=='1'||s[i-1]=='2') dp[j]=dp[j-2];
else
return 0;
}
else
{
if(s[i-1]=='1'||s[i-1]=='2'&&s[i]<='6')
dp[j]=dp[j-1]+dp[j-2];
else
dp[j]=dp[j-1];
}
}
return dp[s.size()];
}
};
  1. 整数拆分
    给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
int integerBreak(int n) {
vector<int>dp(n+1,0);
dp[1]=1;
for(int i=2;i<=n;i++)
for(int j=i-1;j>=1;j--)
{
dp[i]=max(dp[i],dp[j]*(i-j));
dp[i]=max(dp[i],j*(i-j));
}
return dp[n];
}
};

357.计算各个位数不同的数字个数

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
int countNumbersWithUniqueDigits(int n) {
if(n==0)return 1; //n=0时,数组长度为1,运行到dp[1]会指向空地址
vector<int>dp(n+1,0);
dp[0]=1;
dp[1]=10;
for(int i=2;i<=n;i++)
{
dp[i]=dp[i-1]+(dp[i-1]-dp[i-2])*(10-(i-1)); 之前的解加上新增的i位数情况,i位数是由i-1位数加上一位数,再加的一位数只有10-(i-1)种情况。
}
return dp[n];
}
};

以n=3为例,n=2已经计算了0-99之间不重复的数字了,我们需要判断的是100-999之间不重复的数字,那也就只能用10-99之间的不重复的数去组成三位数,而不能使用0-9之间的不重复的数,因为他们也组成不了3位数。而10-99之间不重复的数等于dp[2]-dp[1]。
当i=2时,说明之前选取的数字只有1位,那么我们只要与这一位不重复即可,所以其实有9(10-1)种情况(比如1,后面可以跟0,2,3,4,5,6,7,8,9)。当i=3时,说明之前选取的数字有2位,那么我们需要与2位不重复,所以剩余的有8(10-2)种(比如12,后面可以跟0,3,4,5,6,7,8,9)

1326.灌溉花园的最少水龙头数目

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int dp[10001];
int INF= 0x3f3f3f3f; //定义为无穷大,意味着无法灌溉
int minTaps(int n, vector<int>& ranges) {
memset(dp,INF,sizeof(dp));
dp[0] = 0;
for(int i = 0; i < ranges.size(); i++){
int L = max(0,i-ranges[i]);
int R = min(n,i+ranges[i]);
for(int j = L; j <= R; j++){
dp[j] = min(dp[j],dp[L]+1); //
}
}
return dp[n] == INF ? -1 : dp[n];
}
};
文章作者: Sunxin
文章链接: https://sunxin18.github.io/2020/02/21/leetcode1326/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 lalala
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论