最大单调序列最大值
#include<bits/stdc++.h>
using namespace std;
int dp(vector<int> &nums){
int n=nums.size();
vector<int> p(n,0);
int s=p[0]=nums[0];
for(int i=1;i<n;i++){
p[i]=nums[i];
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
p[i]=max(p[i],p[j]+nums[i]);
}
}
s=max(s,p[i]);
}
return s;
}
int main() {
int n;
while (cin >> n && n != 0) {
vector<int> nums(n);
for (int i = 0; i < n; ++i) {
cin >> nums[i];
}
cout<<dp(nums)<<endl;
}
return 0;
}
- 先初始化
dp
数组,dp[i]
表示以第i
个小姐姐魅力值结尾的符合 “递增” 要求的子序列的最大魅力和。 - 外层循环遍历每个魅力值,内层循环用于查找当前魅力值之前所有比它小的魅力值对应的
dp
值,更新当前dp[i]
,保证其是满足递增条件下的最大和。 - 同时维护一个
max
变量,实时更新全局最大的魅力和。
#include<bits/stdc++.h>
using namespace std;
void ot(int n){
if(n<10)cout<<"0"<<n;
else cout<<n;
}
void time(int o){
int a,b;
a=o/3600+8;
o%=3600;
b=o/60;
o%=60;
if(a>12){
a-=12;
ot(a);
cout<<":";
ot(b);
cout<<":";
ot(o);
cout<<" pm"<<endl;
}
else{
ot(a);
cout<<":";
ot(b);
cout<<":";
ot(o);
cout<<" am"<<endl;
}
}
int main() {
int n,k,a[2005],b[2005],dp[2005];
cin>>n;
while(n--){
cin>>k;
int o=0;
if(k==1)cin>>a[1],o=a[1];
else{
for(int i=1;i<=k;i++)cin>>a[i];
for(int i=2;i<=k;i++)cin>>b[i];
dp[1]=a[1];
for(int i=2;i<=k;i++){
dp[i]=min(dp[i-1]+a[i],dp[i-2]+b[i]);
}
o=dp[k];
}
time(o);
}
return 0;
}
最长有序子数列
#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];
}
vector<int> dp(n, 1);
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (a[j] < a[i]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
int maxLength = 0;
for (int len : dp) {
maxLength = max(maxLength, len);
}
cout << maxLength << endl;
return 0;
}
- 输入处理:程序首先读取序列长度
n
,然后读取序列中的每个元素。 - 动态规划数组
dp
:dp[i]
表示以第i
个元素结尾的最长递增子序列的长度,初始值都设为 1。 - 状态转移:对于每个元素
a[i]
,检查其前面的所有元素a[j]
(其中j < i
)。如果a[j] < a[i]
,则可以将a[i]
接在以a[j]
结尾的子序列后面,形成一个更长的子序列。 - 结果计算:遍历
dp
数组,找出其中的最大值,即为整个序列的最长递增子序列长度
最长公共子序列(长度一致版但是适合大数
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
string a, b;
cin >> a >> b;
// 构建每个字符在a中出现的所有位置(按顺序存储)
vector<int> pos[256];
for (int i = 0; i < n; i++) {
pos[a[i]].push_back(i);
}
// 构建转换后的数组
vector<int> converted;
for (char c : b) {
// 如果字符不在a中出现,跳过
if (pos[c].empty()) continue;
// 对于每个字符,使用其在a中出现的位置的逆序
// 这确保了每个字符最多被使用一次,避免重复匹配
for (auto it = pos[c].rbegin(); it != pos[c].rend(); ++it) {
converted.push_back(*it);
}
}
// 计算LIS
vector<int> f;
for (int x : converted) {
auto it = lower_bound(f.begin(), f.end(), x);
if (it == f.end()) {
f.push_back(x);
} else {
*it = x;
}
}
cout << f.size();
return 0;
}
使用贪心算法和二分查找来计算最长公共子序列的长度
- 数组
f
用于保存递增子序列,f[i]
表示长度为i
的递增子序列末尾元素的最小值。 - 对于字符串
b
中的每个字符,先通过mp
数组找到其在字符串a
中的位置。 - 接着,利用二分查找确定该位置在
f
数组中的插入点,以此来维护递增子序列的性质。
不同长度,多次输入版
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
string s1, s2;
// 持续读取输入,直到文件结束(EOF)
while (cin >> s1 >> s2) {
int m = s1.length();
int n = s2.length();
// 创建一个二维数组dp来存储子问题的解
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
// 填充dp数组
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (s1[i - 1] == s2[j - 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[m][n] << endl;
}
return 0;
}
典型dp,没有前者高效
-
输入处理:
- 借助
while (cin >> s1 >> s2)
达成循环读取输入的目的。 - 此循环会一直运行,直到输入结束(也就是遇到 EOF,在 Windows 系统中可按 Ctrl+Z,在 Unix/Linux 系统中可按 Ctrl+D)。
- 借助
-
动态规划数组:
dp[i][j]
所表示的是s1
的前i
个字符和s2
的前j
个字符的最长公共子序列的长度。
-
状态转移方程:
- 当
s1[i-1]
等于s2[j-1]
时,dp[i][j]
的值为dp[i-1][j-1] + 1
。 - 若不相等,
dp[i][j]
的值为max(dp[i-1][j], dp[i][j-1])
。
- 当
-
输出结果:
- 每次处理完一组输入后,都会输出
dp[m][n]
的值,也就是两个字符串的最长公共子序列的长度。
- 每次处理完一组输入后,都会输出