概述
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为空时无法匹配
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]匹配,即
rbb
^ ^
此时的dp[i][j] = dp[i - 1][j - 1]
或者t[i]不与s[j]匹配,即
rbb
^^
此时的dp[i][j] = dp[i][j - 1]
最后的代码:
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
状态转移方程:
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]结尾的子序列的个数
举例说明:
abca:
a: a
b: ab, b
c: ac, abc, bc, c
a: aa, aba, ba, aca, abca, bca, ca, a(相当于第一个a没做)
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
给定一个字符串数组和两个整数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]即可
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
给定一个整型数组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)
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
给定一个数组,判断其是否能被分成两个和相等的子数组
状态转移方程:
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即可
完整代码:
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中一些经典动态规划题(不定期更新)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复