HDU1159——通用子序列
题目描述
问题 - 1159 (hdu.edu.cn)
程序输入来自文本文件。文件中的每个数据集都包含两个字符串,分别表示给定的序列。序列之间由任意数量的空格分隔。输入数据正确无误。对于每组数据,程序都会在标准输出上打印从单独行的开头开始的最大长度公共子序列的长度。
运行代码
//https://acm.hdu.edu.cn/showproblem.php?pid=1159
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int longest(const string& str1, const string& str2) {
int m = str1.length();
int n = str2.length();
vector<vector<int>> dp(m + 1,vector<int>(n + 1, 0));
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (str1[i - 1] == str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
int main() {
string str1, str2;
while (cin >> str1 >> str2) {
cout << longest(str1, str2) << endl;
}
return 0;
}
代码思路
使用动态规划的思想来求解两个字符串的最长公共子序列(Longest Common Subsequence,LCS)的长度。动态规划是一种通过把原问题分解为相对简单的子问题,并保存子问题的解来避免重复计算,从而解决复杂问题的方法。
-
参数设置:该函数接收两个字符串
str1
和str2
作为参数,代表要计算最长公共子序列的两个序列。 -
初始化动态规划数组:首先获取两个字符串的长度,分别存储在变量
m
和n
中。创建一个二维向量dp
,大小为(m + 1) x (n + 1)
,并初始化为全零。这里的dp[i][j]
表示str1
的前i
个字符和str2
的前j
个字符的最长公共子序列的长度。 -
动态规划过程:使用两个嵌套的循环遍历两个字符串的所有可能子序列组合。如果
str1[i - 1]
(即str1
的第i
个字符)和str2[j - 1]
(即str2
的第j
个字符)相等,说明找到了一个公共字符,此时dp[i][j]
的值应该是dp[i - 1][j - 1] + 1
,也就是两个字符串都去掉当前这个公共字符后的最长公共子序列长度加一。如果两个字符不相等,那么dp[i][j]
的值应该是dp[i - 1][j]
和dp[i][j - 1]
中的较大值。这是因为如果当前字符不相等,最长公共子序列要么来自于不考虑str1
的当前字符(即dp[i - 1][j]
),要么来自于不考虑str2
的当前字符(即dp[i][j - 1]
)。 -
返回结果:最终,
dp[m][n]
就存储了str1
和str2
的最长公共子序列的长度,函数返回这个值。
HDU1160——FatMouse的速度
题目描述
问题 - 1160 (hdu.edu.cn)
运行代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
struct Node {
int w, v, num;
Node(int w, int v, int num) : w(w), v(v), num(num) {}
};
bool compare(const Node& a, const Node& b) {
return a.w < b.w || (a.w == b.w && a.v > b.v);
}
int main() {
std::vector<Node> nodes;
int w, v;
while (std::cin >> w >> v) {
nodes.emplace_back(w, v, nodes.size() + 1);
}
std::sort(nodes.begin(), nodes.end(), compare);
int n = nodes.size();
std::vector<int> dp(n, 1);
std::vector<int> pre(n, -1);
int maxLen = 0, endIndex = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (nodes[j].w < nodes[i].w && nodes[j].v > nodes[i].v && dp[i] < dp[j] + 1) {
dp[i] = dp[j] + 1;
pre[i] = j;
}
}
if (dp[i] > maxLen) {
maxLen = dp[i];
endIndex = i;
}
}
std::cout << maxLen << std::endl;
std::stack<int> st;
while (endIndex!= -1) {
st.push(endIndex);
endIndex = pre[endIndex];
}
while (!st.empty()) {
std::cout << nodes[st.top()].num << std::endl;
st.pop();
}
return 0;
}
代码思路
-
输入与存储:使用
while (std::cin >> w >> v)
循环不断读取输入的重量和价值数据,将其创建为Node
结构体对象,并存储在std::vector<Node> nodes
中。每个Node
结构体包含重量w
、价值v
和编号num
。 -
排序:对
nodes
容器中的物品按照重量w
升序排列,如果重量相等,则按照价值v
降序排列。这样的排序是为了方便后续在遍历物品时,只需要考虑前面的物品是否满足条件即可,因为后面物品的重量一定大于等于前面的物品重量。 -
动态规划求解最长子序列:创建两个长度为
n
(n
为物品数量)的辅助数组dp
和pre
。其中dp[i]
表示以第i
个物品结尾的最长子序列长度,pre[i]
表示最长子序列中第i
个物品的前一个物品在nodes
中的索引。两层循环遍历物品:- 内层循环遍历
i
之前的物品j
,如果物品j
的重量小于物品i
的重量且物品j
的价值大于物品i
的价值,说明物品j
可以作为物品i
的前驱物品。如果此时以物品i
结尾的子序列长度小于以物品j
结尾的子序列长度加一,就更新dp[i]
和pre[i]
。 - 外层循环遍历每个物品
i
。 - 在遍历过程中,记录最长子序列的长度
maxLen
和最长子序列最后一个物品的索引endIndex
。
- 内层循环遍历
-
输出结果:首先输出最长子序列的长度
maxLen
。然后使用栈std::stack<int> st
来逆序输出最长子序列中物品的编号。从最长子序列的最后一个物品索引endIndex
开始,不断根据pre
数组找到前一个物品的索引,将索引压入栈中。最后依次弹出栈中的索引,根据索引在nodes
中找到对应的物品编号并输出。
HDU1165——艾迪的研究 II
题目描述
问题 - 1165 (hdu.edu.cn)
运行代码
#include <iostream>
const int M = 1000005;
int dp[4][M];
void init() {
for (int i = 0; i < M; ++i) {
dp[0][i] = i + 1;
dp[1][i] = i + 2;
dp[2][i] = 2 * i + 3;
}
dp[3][0] = 5;
for (int i = 1; i <= 24; ++i) dp[3][i] = 2 * dp[3][i - 1] + 3;
}
int main() {
init();
int m, n;
while (std::cin >> m >> n) {
std::cout << dp[m][n] << '\n';
}
return 0;
}
代码思路
-
定义常量和数组:
- 定义常量
M
为 1000005,用于表示数组的大小上限。 - 定义二维数组
dp[4][M]
,用于存储阿克曼函数在不同参数下的计算结果。
- 定义常量
-
初始化函数
init()
:使用循环初始化dp
数组:- 当
m = 2
时,dp[2][i] = 2 * i + 3
,即A(2, n) = 2 * n + 3
。 - 当
m = 1
时,dp[1][i] = i + 2
,即A(1, n) = n + 2
。 - 当
m = 0
时,dp[0][i] = i + 1
,即A(0, n) = n + 1
。 - 对于
m = 3
的情况单独处理,先设置dp[3][0] = 5
,然后通过循环计算dp[3][i] = 2 * dp[3][i - 1] + 3
,即A(3, n)
的值是根据前一个n
的值递推得到的,并且限制n
的范围在 24 以内。
- 当
-
主函数
main()
:- 调用
init()
函数进行初始化操作。 - 使用
while (std::cin >> m >> n)
循环不断读取输入的m
和n
的值。 - 对于每一组输入的
m
和n
,直接输出dp[m][n]
,即阿克曼函数A(m, n)
的值。
- 调用