115. Distinct Subsequences
状态转移方程:
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][j−1],s[j]!=t[i]dp[i][j−1]+dp[i−1][j−1],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
70 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
3rbb ^ ^
此时的dp[i][j] = dp[i - 1][j - 1]
或者t[i]不与s[j]匹配,即
1
2
3
4rbb ^^
此时的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
19class 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
状态转移方程:
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
6abca: 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
23const 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
给定一个字符串数组和两个整数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[i−zeroi][j−onei]+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
25class 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
给定一个整型数组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[i−c]+1)
其
中
c
为
c
o
i
n
s
中
的
数
其中c为coins中的数
其中c为coins中的数
比如例题中,
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[11−1]+1,dp[11−2]+1,dp[11−5]+1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class 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
给定一个数组,判断其是否能被分成两个和相等的子数组
状态转移方程:
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[j−nums[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
20class 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中一些经典动态规划题(不定期更新)内容请搜索靠谱客的其他文章。
发表评论 取消回复