题目描述:
给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输入格式:
第一行包含整数 n n n,表示数字三角形的层数。
接下来 n n n 行,每行包含若干整数,其中第 i i i 行表示数字三角形第 i i i 层包含的整数。
输出格式:
输出一个整数,表示最大的路径数字和。
数据范围:
1 ≤ n ≤ 500 , − 10000 ≤ 1≤n≤500,−10000≤ 1≤n≤500,−10000≤三角形中的整数 ≤ 10000 ≤10000 ≤10000
输入样例:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例:
30
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 510;
int a[N][N], f[N][N];
int main()
{
int n;
cin >> n;
memset(a, -0x3f, sizeof a);
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= i; ++j)
{
cin >> a[i][j];
f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j];
}
}
int res = -0x3f;
for (int i = 1; i <= n; ++i) res = max(res, f[n][i]);
cout << res << endl;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 510;
int a[N][N], f[N];
int main()
{
int n;
cin >> n;
memset(a, -0x3f, sizeof a);
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= i; ++j)
cin >> a[i][j];
}
for (int i = 1; i <= n; ++i)
{
for (int j = i; j >= 0; --j)
f[j] = max(f[j - 1], f[j]) + a[i][j];
}
int res = -0x3f;
for (int i = 1; i <= n; ++i) res = max(res, f[i]);
cout << res << endl;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 510;
int a[N][N], f[N][N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j)
cin >> a[i][j];
for (int i = n; i >= 1; --i)
{
for (int j = 1; j <= n; ++j)
f[i][j] = max(f[i + 1][j + 1], f[i + 1][j]) + a[i][j];
}
cout << f[1][1] << endl;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 510;
int a[N][N], f[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j)
cin >> a[i][j];
for (int i = n; i >= 1; --i)
{
for (int j = 1; j <= n; ++j)
f[j] = max(f[j + 1], f[j]) + a[i][j];
}
cout << f[1] << endl;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 510;
int n, f[N][N], a[N][N];
int dfs(int u, int k)
{
if (~f[u][k]) return f[u][k];
if (k > u || u == n + 1) return 0; // 限制范围
return f[u][k] = max(dfs(u + 1, k + 1), dfs(u + 1, k)) + a[u][k];
}
int main()
{
cin >> n;
memset(f, -1, sizeof f);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j) cin >> a[i][j];
cout << dfs(1, 1) << endl;
return 0;
}
题目描述:
给定一个长度为 N N N 的数列,求数值 严格单调递增的子序列 的长度最长是多少。
输入格式:
第一行包含整数 N N N。
第二行包含 N N N 个整数,表示完整序列。
输出格式:
输出一个整数,表示最大长度。
数据范围:
1 ≤ N ≤ 1000 , − 1 0 9 ≤ 1≤N≤1000,−10^9≤ 1≤N≤1000,−109≤数列中的数 ≤ 1 0 9 ≤10^9 ≤109
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 1010;
int f[N], a[N];
int main()
{
int n, res = 1;
cin >> n;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
f[i] = 1;
for (int j = 1; j <= i; ++j)
if (a[i] > a[j]) f[i] = max(f[i], f[j] + 1);
}
cout << f[n] << endl;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 1010;
int n, a[N];
int f[N];
int res, pos; // LIS最大长度 pos:最大长度是哪个下标数字提供
int pre[N]; //记录转移的前序关系
//循环+vector方式打印路径
void print(int k) {
vector<int> path; //因为查找的关系是逆序的,需要用一个向量数组把这个逆序反过来,才能输出
while (k) {
path.push_back(a[k]);
k = pre[k];
}
//倒序输出LIS序列
for (int i = path.size() - 1; i >= 0; i--) printf("%d ", path[i]);
}
//递归方式打印路径
//这个递归输出逆序路径,真是太妙了,把递推与逆序的通用性展示的淋漓尽致,把别人正常用7行才能完成的代码,它只需要2行,真是太精炼了,又好写又好记。
void out(int k) {
if (pre[k]) out(pre[k]); //因为最前面的第1号,它的前序
printf("%d ", a[k]);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
f[i] = 1;
for (int j = 1; j < i; j++)
if (a[i] > a[j] && f[i] <= f[j]) {
f[i] = f[j] + 1;
pre[i] = j; // f[i]的前序是f[j]
}
// 更新最大值
if (f[i] > res) {
res = f[i]; //记录LIS最大值
pos = i; //记录LIS最大值时相应的数组下标i
}
}
//输出LIS最大长度
printf("%d\n", res);
//输出前序数组查看情况
for (int i = 1; i <= n; i++) cout << pre[i] << " ";
puts("");
//循环+vector方式输出LIS路径
//从最大LIS值的下标开始,配合pre数组,不断向前以蛇形方式逆序查找前序
print(pos);
puts("");
//递归方式输出LIS路径
out(pos);
return 0;
}
题目描述:
给定一个长度为 N N N 的数列,求数值 严格单调递增的子序列 的长度最长是多少。
输入格式:
第一行包含整数 N N N。
第二行包含 N N N 个整数,表示完整序列。
输出格式:
输出一个整数,表示最大长度。
数据范围:
1 ≤ N ≤ 100000 1≤N≤100000 1≤N≤100000, − 1 0 9 ≤ −10^9≤ −109≤数列中的数 ≤ 1 0 9 ≤10^9 ≤109
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4
核心思想:
对于同样长度的子串,希望它的末端越小越好,这样后面有更多机会拓展它,才有可能使得数列更长。
算法步骤:
扫描每个原序列中的数字:
如果 q q q 中的最后一个数字 q [ i d x ] q[idx] q[idx] 小于当前数字 a [ i ] a[i] a[i],那么就在 q q q 的最后面增加 a [ i ] a[i] a[i]。
如果 a [ i ] a[i] a[i] 小于 q [ i d x ] q[idx] q[idx],在 q q q 中查找并替换第一个大于等于它元素。
模拟过程:
a | 3 | 1 | 2 | 1 | 8 | 5 | 6 |
---|
开始时 q [ ] q[] q[] 为空,数字3进入序列。
arr | 3 | 1 | 2 | 1 | 8 | 5 | 6 |
---|---|---|---|---|---|---|---|
q | 3 |
1 比 3 小,3出序列 ,1入序列。
a | 3 | 1 | 2 | 1 | 8 | 5 | 6 |
---|---|---|---|---|---|---|---|
q | 1 |
比 1 大,2入序列。
a | 3 | 1 | 2 | 1 | 8 | 5 | 6 |
---|---|---|---|---|---|---|---|
q | 1 |
a | 3 | 1 | 2 | 1 | 8 | 5 | 6 |
---|---|---|---|---|---|---|---|
q | 1 | 2 |
比 2 小,在 q q q 中找到第一个大于等于1的位置,并替换掉原来的数字。
a | 3 | 1 | 2 | 1 | 8 | 5 | 6 |
---|---|---|---|---|---|---|---|
q | 1 | 2 |
8 比 2 大,入序列。
a | 3 | 1 | 2 | 1 | 8 | 5 | 6 |
---|---|---|---|---|---|---|---|
q | 1 | 2 | 8 |
5 比 8 小,在 q q q 中找到第一个大于等于5的数字,并替换掉原来的数字。
a | 3 | 1 | 2 | 1 | 8 | 5 | 6 |
---|---|---|---|---|---|---|---|
q | 1 | 2 | 5 |
6 比 5 大,入序列。
a | 3 | 1 | 2 | 1 | 8 | 5 | 6 |
---|---|---|---|---|---|---|---|
q | 1 | 2 | 5 | 6 |
最后 q q q 的长度 idx + 1
就是最长严格递增子序列的长度。但是 q q q 所存储的序列一般不是最短序列,另类的一种全为最小值的最长严格递增的乱序序列。
例如:
7 3 4 5 1 8 3 输出 :1 4 5 8(保存了最长严格递增子序列的长度)
但是正确答案:3 4 5 8(由于 最小值 1 替代了 3)
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 1e5 + 10;
int q[N], a[N], idx;
int find(int target, int range)
{
int l = 0, r = range;
while (l < r)
{
int mid = l + r >> 1;
if (q[mid] >= target) r = mid;
else l = mid + 1;
}
return l;
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; ++i) cin >> a[i];
q[0] = a[0];
for (int i = 1; i < n; ++i)
{
if (q[idx] < a[i]) q[++idx] = a[i];
else q[find(a[i], idx)] = a[i];
}
cout << idx + 1 << endl;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
using namespace std;
const int N = 1e3 + 10;
int q[N], a[N], pos[N], idx;
void print_path(int n)
{
vector<int> path;
for (int i = n - 1; i >= 0 && idx >= 0; --i)
{
if (pos[i] != idx) continue;
path.push_back(a[i]);
idx--;
}
for (int i = path.size() - 1; i >= 0; --i) cout << path[i] << ' ';
cout << endl;
}
int binary_search(int target, int len)
{
int l = 0, r = len;
while (l < r)
{
int mid = l + (r - l) / 2;
if (q[mid] >= target) r = mid;
else l = mid + 1;
}
return l;
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; ++i) cin >> a[i];
q[0] = a[0];
for (int i = 1; i < n; ++i)
{
if (a[i] > q[idx])
{
q[++idx] = a[i];
pos[i] = idx;
}
else
{
int t = binary_search(a[i], idx);
q[t] = a[i];
pos[i] = t;
}
}
cout << idx + 1 << endl;
print_path(n);
return 0;
}
题目描述:
给定两个长度分别为 N N N 和 M M M 的字符串 A A A 和 B B B,求既是 A A A 的子序列又是 B B B 的子序列的字符串 长度最长 是多少。
输入格式:
第一行包含两个整数 N N N 和 M M M。
第二行包含一个长度为 N N N 的字符串,表示字符串 A A A。
第三行包含一个长度为 M M M 的字符串,表示字符串 B B B。
字符串均由小写字母构成。
输出格式:
输出一个整数,表示最大长度。
数据范围:
1 ≤ N , M ≤ 1000 1≤N,M≤1000 1≤N,M≤1000
输入样例:
4 5
acbd
abedc
输出样例:
3
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 1010;
char a[N], b[N];
int f[N][N];
int main()
{
int n, m;
cin >> n >> m >> a + 1 >> b + 1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
{
if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1;
else f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
cout << f[n][m] << endl;
return 0;
}
题目描述:
给定两个字符串 A A A 和 B B B,现在要将 A A A 经过若干操作变为 B B B,可进行的操作有:
现在请你求出,将 A A A 变为 B B B 至少需要进行多少次操作。
输入格式:
第一行包含整数 n n n,表示字符串 A A A 的长度。
第二行包含一个长度为 n n n 的字符串 A A A。
第三行包含整数 m m m,表示字符串 B B B 的长度。
第四行包含一个长度为 m m m 的字符串 B B B。
字符串中均只包含大小写字母。
输出格式:
输出一个整数,表示最少操作次数。
数据范围:
1 ≤ n , m ≤ 1000 1≤n,m≤1000 1≤n,m≤1000
输入样例:
10
AGTCTGACGC
11
AGTAAGTAGGC
输出样例:
4
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 1010;
int f[N][N];
char a[N], b[N];
int main()
{
int n, m;
cin >> n >> a + 1;
cin >> m >> b + 1;
for (int i = 0; i <= n; ++i) f[i][0] = i; // 提前赋值,避免边界问题
for (int i = 0; i <= m; ++i) f[0][i] = i;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
{
f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1; // 增和减的情况
if (a[i] == b[j]) f[i][j] = min(f[i][j], f[i - 1][j - 1]); // 匹配完全的情况
else f[i][j] = min(f[i][j], f[i - 1][j - 1] + 1); // 改的情况
}
cout << f[n][m] << endl;
return 0;
}
题目描述:
给定 n n n 个长度不超过 10 10 10 的字符串以及 m m m 次询问,每次询问给出一个字符串和一个操作次数上限。
对于每次询问,请你求出给定的 n n n 个字符串中有多少个字符串可以在上限操作次数内经过操作变成询问给出的字符串。
每个对字符串进行的单个字符的插入、删除或替换算作一次操作。
输入格式:
第一行包含两个整数 n n n 和 m m m。
接下来 n n n 行,每行包含一个字符串,表示给定的字符串。
再接下来 m m m 行,每行包含一个字符串和一个整数,表示一次询问。
字符串中只包含小写字母,且长度均不超过 10 10 10。
输出格式:
输出共 m m m 行,每行输出一个整数作为结果,表示一次询问中满足条件的字符串个数。
数据范围:
1 ≤ n , m ≤ 1000 1≤n,m≤1000 1≤n,m≤1000
输入样例:
3 2
abc
acd
bcd
ab 1
acbd 2
输出样例:
1
3
相当于对最短编辑距离的多次运用
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 15, M = 1010;
char str[M][N];
int f[N][N];
int n, m;
int edit_distance(char a[], char b[])
{
int la = strlen(a + 1), lb = strlen(b + 1);
for (int i = 0; i <= la; ++i) f[i][0] = i;
for (int i = 0; i <= lb; ++i) f[0][i] = i;
for (int i = 1; i <= la; ++i)
for (int j = 1; j <= lb; ++j)
{
f[i][j] = min(f[i][j - 1], f[i - 1][j]) + 1;
f[i][j] = min(f[i][j], f[i - 1][j - 1] + (a[i] != b[j]));
}
return f[la][lb];
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; ++i) cin >> (str[i] + 1);
while (m--)
{
int limit, res = 0;
char a[N];
cin >> a + 1 >> limit;
for (int i = 0; i < n; ++i)
if (edit_distance(str[i], a) <= limit) res++;
cout << res << endl;
}
return 0;
}