1. 最大子段和
解题思路:
- 初始化
currentSum
和maxSum
为序列的第一个元素a[0]
。currentSum
用于记录当前正在考虑的连续子段的和,maxSum
用于记录遍历过程中出现过的最大连续子段和。 - 从序列的第二个元素开始(即
i = 1
)进行遍历。在每次循环中,更新currentSum
的值,它等于当前元素a[i]
和currentSum + a[i]
中的较大值。这意味着在当前位置,我们要么选择以当前元素开始一个新的连续子段(即取a[i]
),要么继续扩展之前的连续子段(即取currentSum + a[i]
)。 - 同时,使用
max
函数更新maxSum
,确保它始终记录到目前为止出现过的最大连续子段和.
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 2e5 + 5;
int a[MAXN];
int main() {
int n;
// 读取序列的长度
cin >> n;
// 读取序列中的每个元素
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
// 初始化当前子段和与最大子段和为序列的第一个元素
int currentSum = a[0];
int maxSum = a[0];
// 从第二个元素开始遍历序列
for (int i = 1; i < n; ++i) {
// 更新当前子段和,取当前元素和当前元素加上之前子段和的较大值
currentSum = max(a[i], currentSum + a[i]);
// 更新最大子段和
maxSum = max(maxSum, currentSum);
}
// 输出最大子段和
cout << maxSum << endl;
return 0;
}
2. 采药
解题思路:
- 定义
dp
数组,dp[j]
表示在时间j
内可以获得的最大价值。 - 外层循环遍历每一株草药,内层循环从总时间
T
开始倒序遍历到当前草药的采摘时间timeCost[i]
。这样做是为了确保每株草药只被使用一次。 - 状态转移方程
dp[j] = max(dp[j], dp[j - timeCost[i]] + value[i])
的含义是:对于时间j
,可以选择不采摘当前草药(即保持dp[j]
不变),或者采摘当前草药(即dp[j - timeCost[i]] + value[i]
),取两者中的最大值作为dp[j]
的新值。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX_T = 1005;
const int MAX_M = 105;
// 定义采摘每株草药的时间和价值数组
int timeCost[MAX_M];
int value[MAX_M];
// 定义动态规划数组,dp[i] 表示在时间 i 内可以获得的最大价值
int dp[MAX_T];
int main() {
int T, M;
// 读取总共能够用来采药的时间 T 和山洞里的草药的数目 M
cin >> T >> M;
// 读取每株草药的采摘时间和价值
for (int i = 1; i <= M; ++i) {
cin >> timeCost[i] >> value[i];
}
// 动态规划过程
for (int i = 1; i <= M; ++i) {
// 从总时间 T 开始倒序遍历,避免重复使用同一株草药
for (int j = T; j >= timeCost[i]; --j) {
// 状态转移方程,更新在时间 j 内的最大价值
dp[j] = max(dp[j], dp[j - timeCost[i]] + value[i]);
}
}
// 输出在规定时间 T 内可以采到的草药的最大总价值
cout << dp[T] << endl;
return 0;
}
3.宝物筛选
解题思路:
- 定义
dp
数组,dp[j]
表示载重为j
时的最大价值。 - 遍历所有经过二进制优化后的物品,对于每个物品,从最大载重
W
开始倒序遍历到该物品的重量,使用状态转移方程dp[j] = max(dp[j], dp[j - item.weight] + item.value)
更新dp[j]
的值,确保不超过当前载重且价值最大。
代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 定义物品结构体,包含价值、重量
struct Item {
int value;
int weight;
};
int main() {
int n, W;
// 读取宝物种数 n 和采集车的最大载重 W
cin >> n >> W;
vector<Item> items;
// 处理每种宝物
for (int i = 0; i < n; ++i) {
int v, w, m;
// 读取每种宝物的价值 v、重量 w 和数量 m
cin >> v >> w >> m;
// 二进制优化
for (int k = 1; k <= m; k *= 2) {
items.push_back({k * v, k * w});
m -= k;
}
if (m > 0) {
items.push_back({m * v, m * w});
}
}
// 动态规划数组,dp[j] 表示载重为 j 时的最大价值
vector<int> dp(W + 1, 0);
// 遍历所有物品
for (const auto& item : items) {
// 倒序遍历载重,避免重复使用同一物品
for (int j = W; j >= item.weight; --j) {
// 状态转移方程,更新最大价值
dp[j] = max(dp[j], dp[j - item.weight] + item.value);
}
}
// 输出载重为 W 时的最大价值
cout << dp[W] << endl;
return 0;
}
4. 最长公共子序列
解题思路:
创建一个大小为 (n + 1) x (n + 1)
的二维 std::vector
数组 dp
,并初始化为全 0。dp[i][j]
表示 P1
的前 i
个元素和 P2
的前 j
个元素的最长公共子序列的长度。 使用两层嵌套的 for
循环遍历所有可能的 i
和 j
。当 P1[i - 1]
等于 P2[j - 1]
时,说明当前元素是公共元素,dp[i][j]
的值等于 dp[i - 1][j - 1]
加 1。当 P1[i - 1]
不等于 P2[j - 1]
时,dp[i][j]
取 dp[i - 1][j]
和 dp[i][j - 1]
中的较大值。
代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 二分查找,找到第一个大于等于 x 的位置
int binarySearch(vector<int>& tail, int x) {
int left = 0, right = tail.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (tail[mid] < x) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
// 求最长上升子序列的长度
int lengthOfLIS(vector<int>& nums) {
if (nums.empty()) return 0;
vector<int> tail;
tail.push_back(nums[0]);
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] > tail.back()) {
tail.push_back(nums[i]);
} else {
int pos = binarySearch(tail, nums[i]);
tail[pos] = nums[i];
}
}
return tail.size();
}
int main() {
int n;
cin >> n;
vector<int> P1(n), P2(n);
// 读取第一个排列 P1
for (int i = 0; i < n; ++i) {
cin >> P1[i];
}
// 读取第二个排列 P2
for (int i = 0; i < n; ++i) {
cin >> P2[i];
}
// 记录 P1 中每个数字的位置
vector<int> pos(n + 1);
for (int i = 0; i < n; ++i) {
pos[P1[i]] = i;
}
// 将 P2 中每个数字替换为其在 P1 中的位置
vector<int> newP2;
for (int i = 0; i < n; ++i) {
newP2.push_back(pos[P2[i]]);
}
// 求新序列的最长上升子序列的长度
int result = lengthOfLIS(newP2);
cout << result << endl;
return 0;
}
学习总结:
-
求最长上升子序列的长度:
int len = 0;
for (int i = 0; i < n; ++i)
{
if (dp[len] < A[i])
dp[++len] = A[i];
else
*lower_bound(dp + 1, dp + len + 1, A[i]) = A[i];
}
求最长下降子序列长度:
*lower_bound(dp + 1, dp + len + 1, A[i], greater<int>()) = A[i];//greater<int>() 作为 lower_bound 的比较函数,改变了其默认的查找规则,使其能在降序序列中找到第一个不大于目标值的元素位置。
求最长不上升或不下降子序列的长度使用 upper_bound()
函数。