线性动态规划
1.1最长公共子序列
1.1.1 二维DP推算
#include <iostream>
#include <vector>
typedef long long ll; // 使用long long类型以处理大数
using namespace std;
int main() {
ll n;
cin >> n; // 输入序列的长度
// 创建两个向量来存储输入的序列
vector<ll> a(n);
vector<ll> b(n);
// 读取第一个序列
for (ll i = 0; i < n; ++i) cin >> a[i];
// 读取第二个序列
for (ll i = 0; i < n; ++i) cin >> b[i];
// 创建DP表,大小为(n+1) x (n+1),初始化为0
vector<vector<ll>> dp(n+1, vector<ll>(n+1, 0));
// 填充DP表
for (ll i = 1; i <= n; ++i) {
for (ll j = 1; j <= n; ++j) {
if (a[i-1] == b[j-1]) {
// 如果当前字符匹配,则LCS长度加1
dp[i][j] = dp[i-1][j-1] + 1;
} else {
// 如果不匹配,则取左边或上边的最大值
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
// 输出最长公共子序列的长度
cout << dp[n][n] << endl;
return 0;
}
1.1.2 二分优化
题目链接:(洛谷):
(1)
#include <iostream>
#include <vector>
#include <algorithm>
typedef long long ll;
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
ll n;
cin>>n;
vector<ll>a(n);
vector<ll>b(n);
vector<ll>pos(1e5+5);
for(ll i=0;i<n;++i) cin>>a[i];
for(ll i=0;i<n;++i){
cin>>b[i];
pos[b[i]]=i;
}
vector<ll>dp;
for(ll i=0;i<n;++i){
ll t=pos[a[i]];
auto it=lower_bound(dp.begin(), dp.end(),t);
if(it==dp.end()) dp.push_back(t);
else *it=t;
}
cout<<dp.size()<<endl;
return 0;
}
(2)
#include <iostream>
#include <vector>
#include <algorithm>
typedef long long ll;
using namespace std;
// 自定义二分查找函数,替代 lower_bound
ll binarySearch(const vector<ll>& v, ll target) {
ll left = 0, right = v.size() - 1;
while (left <= right) {
ll mid = left + (right - left) / 2;
if (v[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
vector<ll> v;
ll n;
while (cin >> n) {
v.push_back(n);
}
ll p = v.size();
reverse(v.begin(), v.end());
vector<ll> dp;
for (ll i = 0; i < p; ++i) {
ll pos = binarySearch(dp, v[i]);
if (pos == dp.size()) {
dp.push_back(v[i]);
} else {
dp[pos] = v[i];
}
}
cout << dp.size() << endl;
return 0;
}
1.1.3字符串类型最长不下降子序列
(1)DP正常版本
https://www.luogu.com.cn/problem/P1435
1.2最长不下降子序列(LIS)
P1091 [NOIP2004 提高组] 合唱队形
(1)正常版本
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> height(n);
for (int i = 0; i < n; ++i) {
cin >> height[i];
}
vector<int> left(n, 1), right(n, 1);
// 计算 left 数组
for (int i = 1; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (height[i] > height[j]) {
left[i] = max(left[i], left[j] + 1);
}
}
}
// 计算 right 数组
for (int i = n - 2; i >= 0; --i) {
for (int j = n - 1; j > i; --j) {
if (height[i] > height[j]) {
right[i] = max(right[i], right[j] + 1);
}
}
}
// 计算最大合唱队形的长度
int max_len = 0;
for (int i = 0; i < n; ++i) {
max_len = max(max_len, left[i] + right[i] - 1);
}
// 需要移除的最少同学数量
int result = n - max_len;
cout << result << endl;
return 0;
}
(2)二分版本
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 二分查找函数,用于找到大于等于target的第一个元素的位置
int binarySearch(const vector<int>& arr, int target) {
int left = 0, right = arr.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
int main() {
int n;
cin >> n;
vector<int> heights(n);
for (int i = 0; i < n; i++) {
cin >> heights[i];
}
// 计算从左到右的最长递增子序列
vector<int> lis(n, 1);
vector<int> lisSequence;
for (int i = 0; i < n; i++) {
int pos = binarySearch(lisSequence, heights[i]);
if (pos == lisSequence.size()) {
lisSequence.push_back(heights[i]);
} else {
lisSequence[pos] = heights[i];
}
lis[i] = pos + 1;
}
// 计算从右到左的最长递增子序列
vector<int> lds(n, 1);
vector<int> ldsSequence;
for (int i = n - 1; i >= 0; i--) {
int pos = binarySearch(ldsSequence, heights[i]);
if (pos == ldsSequence.size()) {
ldsSequence.push_back(heights[i]);
} else {
ldsSequence[pos] = heights[i];
}
lds[i] = pos + 1;
}
// 找到最长的"山形"子序列
int maxMountain = 0;
for (int i = 0; i < n; i++) {
maxMountain = max(maxMountain, lis[i] + lds[i] - 1);
}
// 计算需要出列的同学数
cout << n - maxMountain << endl;
return 0;
}