线性DP:最长公共子序列 (LCS)
最长公共子序列
题目描述
给定一个长度为 NN 数组 aa 和一个长度为 MM 的数组 bb。
请你求出它们的最长公共子序列长度为多少。
输入描述
输入第一行包含两个整数 N,MN,M,分别表示数组 aa 和 bb 的长度。
第二行包含 NN 个整数 a1,a2,...,ana1,a2,...,an。
第三行包含 MM 个整数 b1,b2,...,bnb1,b2,...,bn。
1≤N,M≤1031≤N,M≤103,1≤ai,bi≤1091≤ai,bi≤109。
输出描述
输出一行整数表示答案。
输入输出样例
示例 1
输入
5 6
1 2 3 4 5
2 3 2 1 4 5
输出
4
#include <iostream>
#include <vector>
using namespace std;
int main() {
int N, M;
cin >> N >> M;
vector<int> a(N), b(M);
for (int i = 0; i < N; ++i) cin >> a[i];
for (int i = 0; i < M; ++i) cin >> b[i];
// dp[i][j]表示a[0..i-1]和b[0..j-1]的最长公共子序列长度
vector<vector<int>> dp(N + 1, vector<int>(M + 1, 0));
// 动态规划求解
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= M; ++j) {
if (a[i - 1] == b[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// 最长公共子序列的长度在dp[N][M]中
cout << dp[N][M] << endl;
return 0;
}
线性DP:最长递增子序列(LIS)
蓝桥骑士
题目描述
小明是蓝桥王国的骑士,他喜欢不断突破自我。
这天蓝桥国王给他安排了 NN 个对手,他们的战力值分别为 a1,a2,...,ana1,a2,...,an,且按顺序阻挡在小明的前方。对于这些对手小明可以选择挑战,也可以选择避战。
身为高傲的骑士,小明从不走回头路,且只愿意挑战战力值越来越高的对手。
请你算算小明最多会挑战多少名对手。
输入描述
输入第一行包含一个整数 NN,表示对手的个数。
第二行包含 NN 个整数 a1,a2,...,ana1,a2,...,an,分别表示对手的战力值。
1≤N≤3×1051≤N≤3×105,1≤ai≤1091≤ai≤109。
输出描述
输出一行整数表示答案。
输入输出样例
示例 1
输入
6
1 4 2 2 5 6
输出
4
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int N;
cin >> N;
vector<int> a(N);
for (int i = 0; i < N; ++i) {
cin >> a[i];
}
// lis数组存储的是递增子序列的最后一个元素
vector<int> lis;
for (int i = 0; i < N; ++i) {
// 使用lower_bound查找a[i]应该插入的位置
auto it = lower_bound(lis.begin(), lis.end(), a[i]);
if (it == lis.end()) {
// 如果没有找到合适的位置,就将a[i]加入lis的末尾
lis.push_back(a[i]);
} else {
// 否则,替换掉最小的一个大于或等于a[i]的元素
*it = a[i];
}
}
// lis的大小即为最长递增子序列的长度
cout << lis.size() << endl;
return 0;
}
背包DP:0/1 背包问题
题目描述
小明有一个容量为 VV 的背包。
这天他去商场购物,商场一共有 NN 件物品,第 ii 件物品的体积为 wiwi,价值为 vivi。
小明想知道在购买的物品总体积不超过 VV 的情况下所能获得的最大价值为多少,请你帮他算算。
输入描述
输入第 11 行包含两个正整数 N,VN,V,表示商场物品的数量和小明的背包容量。
第 2∼N+12∼N+1 行包含 22 个正整数 w,vw,v,表示物品的体积和价值。
1≤N≤1021≤N≤102,1≤V≤1031≤V≤103,1≤wi,vi≤1031≤wi,vi≤103。
输出描述
输出一行整数表示小明所能获得的最大价值。
输入输出样例
示例 1
输入
5 20
1 6
2 5
3 8
5 15
3 3
输出
37
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int N, V;
cin >> N >> V;
// dp[i]表示容量为i的背包能装的最大价值
vector<int> dp(V + 1, 0);
// 输入物品的体积和价值
for (int i = 0; i < N; ++i) {
int w, v;
cin >> w >> v;
// 倒序更新dp数组,避免重复使用物品
for (int j = V; j >= w; --j) {
dp[j] = max(dp[j], dp[j - w] + v);
}
}
// 最终的最大价值为dp[V]
cout << dp[V] << endl;
return 0;
}
背包DP:完全背包问题
问题描述
有 NN 件物品和一个体积为 MM 的背包。第 ii 个物品的体积为 vivi,价值为 wiwi。每件物品可以使用无限次。
请问可以通过什么样的方式选择物品,使得物品总体积不超过 MM 的情况下总价值最大,输出这个最大价值即可。
输入格式
第一行输入两个正整数 N,MN,M。(1≤N,M≤1000)(1≤N,M≤1000)
接下来 NN 行,每行输入两个整数 vi,wivi,wi。(0≤vi,wi≤1000)(0≤vi,wi≤1000)
输出格式
输出一个整数,表示符合题目要求的最大价值。
样例输入
4 5
1 2
2 4
3 4
4 5
样例输出
10
说明
你可以选择 11 个第一个物品和 22 个第二个物品。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int N, M;
cin >> N >> M;
vector<int> dp(M + 1, 0); // dp[i]表示容量为i的背包能获得的最大价值
// 输入物品的体积和价值
for (int i = 0; i < N; ++i) {
int v, w;
cin >> v >> w;
// 完全背包:从v到M遍历更新dp数组
for (int j = v; j <= M; ++j) {
dp[j] = max(dp[j], dp[j - v] + w);
}
}
// 最终的最大价值为dp[M]
cout << dp[M] << endl;
return 0;
}