我是靠谱客的博主 冷酷钻石,这篇文章主要介绍leetcode中一些经典动态规划题(不定期更新),现在分享给大家,希望可以做个参考。

115. Distinct Subsequences
115
状态转移方程:
d p [ i ] [ j ] = { d p [ i ] [ j − 1 ] , s [ j ] ! = t [ i ] d p [ i ] [ j − 1 ] + d p [ i − 1 ] [ j − 1 ] , s [ j ] = = t [ i ] dp[i][j]=begin{cases} dp[i][j - 1],qquadqquadqquadqquadquad s[j] !=t[i] \\ dp[i][j - 1] + dp[i - 1][j - 1],quad s[j] ==t[i] end{cases} dp[i][j]=dp[i][j1],s[j]!=t[i]dp[i][j1]+dp[i1][j1],s[j]==t[i]
理解:
dp[i][j]表示s[0:j]和t[0:i]的结果
当s[j] 与 t[i]不相等时,当前结果即为前一轮结果
当两者相等时,当前结果可以分为两个情况:
s[j] 与 t[i] 匹配,此时对应的结果为dp[i - 1][j - 1]
s[j] 不与 t[i] 匹配,此时的结果为dp[i][j - 1]
因此最终的结果为两者的和
举个例子:
先补一行一列,第0行表示当t为空时,都可以匹配,第0列表示当s为空时无法匹配

复制代码
1
2
3
4
5
6
7
0 r b b b t (s) 0 1 1 1 1 1 1 r 0 1 1 1 1 1 b 0 0 1 2 3 3 t 0 0 0 0 0 3 (t)

s = rbbbt,t=rbt,当遍历到t[i] = ‘b’ s[j]=‘b’(第二个)时,此时有两个选择:t[i]与s[j]匹配,即

复制代码
1
2
3
rbb ^ ^

此时的dp[i][j] = dp[i - 1][j - 1]
或者t[i]不与s[j]匹配,即

复制代码
1
2
3
4
rbb ^^

此时的dp[i][j] = dp[i][j - 1]
最后的代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution { public: int numDistinct(string s, string t) { int lenS = s.length(), lenT = t.length(); vector<vector<long long>> dp(lenT + 1, vector<long long>(lenS + 1, 0)); for(int i = 0; i <= lenS; ++i) dp[0][i] = 1; for(int i = 1; i <= lenT; ++i) dp[i][0] = 0; for(int i = 1; i <= lenT; ++i) for(int j = 1; j <= lenS; ++j) { if(s[j - 1] == t[i - 1]) dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1]; else dp[i][j] = dp[i][j - 1]; } return dp[lenT][lenS]; } };

940. Distinct Subsequences II
940
状态转移方程:
d p [ i ] = { d p [ i ] + d p [ j ] , s [ j ] ! = s [ i ] d o n o t h i n g , s [ j ] = = t [ i ] dp[i]=begin{cases} dp[i] + dp[j],quad s[j] !=s[i] \\ do quad nothing,quad s[j] ==t[i] end{cases} dp[i]=dp[i]+dp[j],s[j]!=s[i]donothing,s[j]==t[i]
j的取值:[0, i)
理由:
dp[i]表示以s[i]结尾的子序列的个数
举例说明:

复制代码
1
2
3
4
5
6
abca: a: a b: ab, b c: ac, abc, bc, c a: aa, aba, ba, aca, abca, bca, ca, a(相当于第一个a没做)
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const int MOD = 1e9 + 7; class Solution { public: int distinctSubseqII(string S) { int result = 0; vector<int> dp(S.length(), 1); for(unsigned i = 0; i < S.length(); ++i) { for(unsigned j = 0; j < i; ++j) { if(S[j] != S[i]) { dp[i] += dp[j]; dp[i] %= MOD; } } result += dp[i]; result %= MOD; } return result; } };

474. Ones and Zeroes
474
给定一个字符串数组和两个整数m,n,其中m表示0能出现的次数上限,n表示1能出现的次数上限,给出最多数量的字符串,使其1和0的个数都满足要求
典型的二维费用背包问题,先创建一个二维数组res, res[i][j]表示只使用i个0和j个1,能得到的最大价值,每次遇到一个字符串,先分别算出0和1的数目,作为二维费用中各自的cost,且每个元素的value均为1
因此 r e s [ i ] [ j ] = m a x ( r e s [ i ] [ j ] , r e s [ i − z e r o i ] [ j − o n e i ] + 1 ) res[i][j] = max(res[i][j], res[i - zero_i][j - one_i]+1) res[i][j]=max(res[i][j],res[izeroi][jonei]+1),最后返回res[m][n]即可

复制代码
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
class Solution { public: int findMaxForm(vector<string>& strs, int m, int n) { int len = strs.size(); vector<vector<int>> res(m + 1, vector<int>(n + 1, 0)); //int maxn = 0, pos = 1; for (string s : strs) { int zero = 0, one = 0; for (char c : s) if (c == '0') zero++; else one++; for(int i = m; i >= zero; --i) for (int j = n; j >= one; --j) { res[i][j] = max(res[i][j], res[i - zero][j - one] + 1); //maxn = max(res[i][j], maxn); } //pos++; } return res[m][n]; } };

322. Coin Change
322
给定一个整型数组coins和一个整数amount,其中coins中的数可以用无限次,判断最少需要使用多少个coins中的数,使其之和为amount,若不存在,则返回-1。
状态转移方程:
d p [ i ] = m i n ( d p [ i ] , d p [ i − c ] + 1 ) dp[i]=min(dp[i], dp[i-c]+1) dp[i]=min(dp[i],dp[ic]+1)
其 中 c 为 c o i n s 中 的 数 其中c为coins中的数 ccoins
比如例题中, d p [ 11 ] = m i n ( d p [ 11 ] , d p [ 11 − 1 ] + 1 , d p [ 11 − 2 ] + 1 , d p [ 11 − 5 ] + 1 ) dp[11]=min(dp[11], dp[11-1]+1, dp[11-2]+1, dp[11-5]+1) dp[11]=min(dp[11],dp[111]+1,dp[112]+1,dp[115]+1)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution { public: int coinChange(vector<int>& coins, int amount) { vector<int> dp(amount + 1, 1e9); sort(coins.begin(), coins.end()); dp[0] = 0; for(int i = 1; i <= amount; ++i) { int minn = 1e9; for(int c: coins) { if(i - c < 0) break; minn = min(minn, dp[i - c]); } dp[i] = minn + 1; } if(dp[amount] >= 1e9) return -1; else return dp[amount]; } };

416. Partition Equal Subset Sum
416
给定一个数组,判断其是否能被分成两个和相等的子数组
状态转移方程:
d p [ j ] = d p [ j ] ∣ ∣ d p [ j − n u m s [ i ] ] dp[j] =dp[j] || dp[j - nums[i]] dp[j]=dp[j]dp[jnums[i]]
举个例子:[2, 2, 3, 4, 5]
初始化dp[0]=true
第一轮dp[2] = dp[2] || dp[2-2] = true
第二轮dp[4] = dp[4] || dp[4-2] = true
即后一项对应不使用该元素时的和
最后只要返回原数组总和sum 的 1/2即可
完整代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution { public: bool canPartition(vector<int>& nums) { int len = nums.size(); if(len < 2) return false; long long int sum = 0; for(int& i: nums) sum += i; if(sum % 2) return false; // sum为奇数,则不可划分 sum /= 2; vector<bool> dp(sum + 1, 0); dp[0] = true; // 初始化dp[0] = true for(int i = 0; i < len; ++i) { for(int j = sum; j >= nums[i]; --j) dp[j] = dp[j] || dp[j - nums[i]]; } return dp[sum]; } };

最后

以上就是冷酷钻石最近收集整理的关于leetcode中一些经典动态规划题(不定期更新)的全部内容,更多相关leetcode中一些经典动态规划题(不定期更新)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(60)

评论列表共有 0 条评论

立即
投稿
返回
顶部