实验2.1 数字三角形问题
问题描述:给定一个由n行数字组成的数字三角形,如下图所示,试设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
算法设计:对于给定的由n行数字组成的数字三角形,计算从三角形的顶至底的路径经过的数字和的最大值。
输入:
第1行是数字三角形的行数n,接下来n行是数字三角形各行中的数字。
输出:
数字和的最大值及最大值由哪些数字的和组成。
实例:
输入:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出:
30
7 3 8 7 5
#include <iostream>
#include <vector>
using namespace std;
int m(vector<vector<int> >& t) {
int n = t.size();
vector<vector<int> > d(n, vector<int>(n, 0));
for (int i = 0; i < n; i++) {
d[n - 1][i] = t[n - 1][i];
}
for (int i = n - 2; i >= 0; i--) {
for (int j = 0; j <= i; j++) {
d[i][j] = max(d[i + 1][j], d[i + 1][j + 1]) + t[i][j];
}
}
int r = d[0][0];
vector<int> p;
int x = 0, y = 0;
p.push_back(t[x][y]);
while (x < n - 1) {
if (d[x + 1][y] > d[x + 1][y + 1]) {
x++;
} else {
x++;
y++;
}
p.push_back(t[x][y]);
}
cout<<r<<endl;
for (int i = 0; i < p.size(); i++) {
cout << p[i] << " ";
}
cout << endl;
return r;
}
int main() {
int n;
cin >> n;
vector<vector<int> > t(n, vector<int>(n, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i; j++) {
cin >> t[i][j];
}
}
m(t);
return 0;
}
以下是对代码的具体分析:
1. m函数:
- 该函数接受一个二维整数向量 `t` 作为参数,表示数字三角形的各行数字。
- 首先,获取数字三角形的行数 `n`。
- 创建一个二维数组 `d` 用于保存每个位置的最大路径和,初始化为全零。
- 将最底层的路径和初始化为数字三角形最后一行的值。
- 从倒数第二层开始,自底向上计算最大路径和。对于每个位置 `(i, j)`,将其最大路径和设为下一层相邻两个位置的最大路径和中较大的那个加上当前位置的值。
- 获取顶部位置的最大路径和,即为数字三角形的最大路径和。
- 根据最大路径和回溯得到路径,并输出路径和最大值。
2. main 函数:
- 该函数负责程序的输入和输出。
- 首先,提示用户输入数字三角形的行数,并读取输入值。
- 创建一个二维整数向量 `t` 用于保存数字三角形的各行数字。
- 提示用户输入数字三角形各行中的数字,并读取输入值。
- 调用 `m` 函数计算数字三角形的最大路径和,并输出结果。
实验2.2 最长公共子序列
问题描述:
给定两个序列X={x1,x2,...,xm}和Y={y1,y2,...,yn},找出X和Y的最长公共子序列。
输入:
第1行:两个子序列的长度,m n
第2行:第1个子序列的各个元素(序列下标从1开始)
第3行:第2个子序列的各个元素(序列下标从1开始)
输出:
最长公共子序列
实例:
输入:
第1行:
4 5 //m和n的值
第2行
abad //输入4个字符,下标从1开始
第3行
baade //输入5个字符,下标从1开始
输出:
aad
#include <iostream>
using namespace std;
void LCSLength(int m, int n, char *x, char *y, int **c, int **b) {
for (int i = 0; i <= m; i++) c[i][0] = 0;
for (int i = 0; i <= n; i++) c[0][i] = 0;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (x[i - 1] == y[j - 1]) {
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = '#';
} else if (c[i - 1][j] >= c[i][j - 1]) {
c[i][j] = c[i - 1][j];
b[i][j] = '@';
} else {
c[i][j] = c[i][j - 1];
b[i][j] = '%';
}
}
}
}
void LCS(int i, int j, char *x, int **b) {
if (i == 0 || j == 0) return;
if (b[i][j] == '#') {
LCS(i - 1, j - 1, x, b);
cout << x[i - 1];
} else if (b[i][j] == '@') {
LCS(i - 1, j, x, b);
} else {
LCS(i, j - 1, x, b);
}
}
int main() {
int m, n;
cin >> m >> n;
char x[m], y[n];
for (int i = 0; i < m; i++) cin >> x[i];
for (int i = 0; i < n; i++) cin >> y[i];
int **c = new int*[m + 1];
int **b = new int*[m + 1];
for (int i = 0; i <= m; i++) {
c[i] = new int[n + 1];
b[i] = new int[n + 1];
}
LCSLength(m, n, x, y, c, b);
LCS(m, n, x, b);
for (int i = 0; i <= m; i++) {
delete[] c[i];
delete[] b[i];
}
delete[] c;
delete[] b;
return 0;
}
下面是对代码的具体分析:
1. `LCSLength` 函数:
- 这个函数用于计算两个字符串的最长公共子序列的长度。
- 使用动态规划的方法,创建了两个二维数组 `c` 和 `b`,分别用于保存最长公共子序列的长度和最优解路径的标记。
- 遍历两个字符串,通过填充 `c` 和 `b` 数组来计算最长公共子序列的长度以及最优解路径的标记。
- 如果 `x[i - 1]` 等于 `y[j - 1]`,则说明当前字符属于最长公共子序列,将 `c[i][j]` 设置为左上方元素的值加1,并将 `b[i][j]` 设置为 `#`。
- 否则,根据动态规划的原理,选择左边或上边元素中较大的值作为 `c[i][j]` 的值,并根据选择情况在 `b[i][j]` 中标记相应的方向。
2. `LCS` 函数:
- 这个函数用于输出最长公共子序列。
- 使用递归的方法,根据 `b` 数组的标记逆向回溯,输出最长公共子序列。
- 如果 `b[i][j]` 的值为 `#`,表示当前字符属于最长公共子序列,递归调用 `LCS` 函数以输出前一个字符,然后输出当前字符。
- 如果 `b[i][j]` 的值为 `@`,表示当前字符不属于最长公共子序列,递归调用 `LCS` 函数以输出上一行的对应位置字符。
- 如果 `b[i][j]` 的值为 `%`,表示当前字符不属于最长公共子序列,递归调用 `LCS` 函数以输出当前行的上一列对应位置字符。
3. `main` 函数:
- `main` 函数首先从标准输入读取两个整数 `m` 和 `n`,分别表示两个字符串的长度。
- 然后,创建两个字符数组 `x` 和 `y` 分别用于存储这两个字符串。
- 通过循环从标准输入读取字符串的内容。
- 动态申请了两个二维数组 `c` 和 `b` 作为动态规划的辅助数组。
- 调用 `LCSLength` 函数计算两个字符串的最长公共子序列的长度,并调用 `LCS` 函数输出最长公共子序列。
- 最后释放动态申请的内存。