概述
目录
- A : 爬台阶
- B : 拿数问题
- C : 矩阵选数
- D : 最长上升子序列
- E : 最长公共子序列
- 思路
A : 爬台阶
题目描述
楼上有 nn 级台阶,其中有 mm 级台阶是不安全的。yhf一开始站在第 00 级台阶上,希望最终走到第 nn 级台阶
yhf跨一步满足以下约束:
只能向前走
不能落脚在不安全的台阶上
最多迈 kk 级台阶
落脚点不能超过第 nn 级台阶
也就是说,若某一刻yhf站在第 cc 级台阶上,那么他下一步可以落脚的位置 xx 满足 c < x le min(c + k, n)c<x≤min(c+k,n) 且第 xx 级台阶是安全的。那么,yhf有多少种方法走到第 nn 级台阶
image.png
输入格式
第一行三个整数 n, m, kn,m,k ,描述见上文
第二行 mm 个整数,d_1, d_2 ,…,d_md
1
,d
2
,…,d
m
,其中 1 le d_i < n1≤d
i
<n ,表示不安全台阶的编号
输出格式
一个整数,模 998244353998244353 后的方案数
测试样例
样例输入
5 1 2
3
样例输出
2
数据规模
对于 100 %100% 的数据,1 le m < n le 10^6, 1 le k le 10^61≤m<n≤10
6
,1≤k≤10
6
#include <iostream>
#include <cstdio>
#include <set>
const long long mod = 998244353;
typedef long long unn;
long long k = 0;
unn gos[9999999];
std::set<unn> lim;
void caculate(unn n)
{
gos[0] = 1;
unn sum = 0;
for (unn i = 1; i <= n; i++)
{
sum += gos[i - 1];
if (i - k - 1 >= 0)
{
sum -= gos[i - k - 1];
}
sum = (mod + sum % mod) % mod;
/*for (unn j = 1; j <= k; j++)
{
if (i - j >= 0)
{
gos[i] = gos[i] + gos[i - j];
gos[i] %= mod;
}
}*/
if (lim.find(i) != lim.end())
{
continue;
}
gos[i] = sum;
}
}
int main()
{
unn n, m, _k;
scanf("%lld %lld %lld", &n, &m, &_k);
k = _k;
for (unn i = 0; i < m; i++)
{
unn ll;
scanf("%lld", &ll);
lim.insert(ll);
}
caculate(n);
printf("%lldn", gos[n]);
return 0;
}
B : 拿数问题
题目描述
给定 nn 个数,我们要从中选出多若干个数,其中任意大小不同的两数差的绝对值不能为 11,那么选出的数之和最大是多少。
输入格式
第一行一个整数 nn 。
第二行 nn 个整数,a_1,a_2 ,…,a_na
1
,a
2
,…,a
n
,表示这 nn 个数
输出格式
一个整数,选出的数的和的最大值
测试样例
样例输入
5
1 1 3 2 3
样例输出
8
数据规模
对于 100 %100% 的数据,1 le n le 10^6, 1 le a_i le 10^61≤n≤10
6
,1≤a
i
≤10
6
#include <iostream>
#include <cstdio>
#define MAX(a, b) (a > b ? a : b)
typedef long long unn;
unn num[9999999];
unn score[9999999];
int main()
{
unn n;
unn N = 0;
scanf("%lld", &n);
while (n > 0)
{
unn a;
scanf("%lld", &a);
N = MAX(N, a);
num[a] += 1;
n--;
}
score[0] = 0;
score[1] = num[1] * 1;
for (unn i = 2; i <= N; i++)
{
score[i] = MAX(score[i - 1], score[i - 2] + num[i] * i);
}
printf("%lldn", score[N]);
return 0;
}
C : 矩阵选数
题目描述
给定一个 33 行,nn 列的矩阵,我们要在矩阵的每一列选一个数。对于第 i(1 le i le n)i(1≤i≤n) 列,我们令 d_id
i
为第 ii 列选择的数。那么,sum_1^{n-1}|d_i - d_{i+1}|∑
1
n−1
∣d
i
−d
i+1
∣ 最小是多少
输入格式
第一行一个整数 nn ,描述见上文
后面三行每行 nn 个整数,为矩阵的各个元素
输出格式
一个整数,题目要求的最小值
测试样例
样例输入
5
5 10 5 4 4
1 7 8 4 0
3 4 9 0 3
样例输出
3
数据规模
对于 100 %100% 的数据,1 le n le 10^61≤n≤10
6 ,矩阵中数据绝对值不超过 10^610 6
#include <iostream>
typedef long long unn;
unn n[3][9999999];
unn df[3][9999999];
unn max(unn a, unn b, unn c)
{
return a > b ? (a > c ? a : c) : (b > c ? b : c);
}
unn min(unn a, unn b, unn c)
{
return -max(-a, -b, -c);
}
unn abs(unn a)
{
return a > 0 ? a : -a;
}
void toDiff(unn N)
{
df[0][0] = 0;
df[1][0] = 0;
df[2][0] = 0;
for (unn i = 1; i < N; i++)
{
for (unn j = 0; j < 3; j++)
{
df[j][i] = min(df[0][i - 1] + abs(n[j][i] - n[0][i - 1]),
df[1][i - 1] + abs(n[j][i] - n[1][i - 1]),
df[2][i - 1] + abs(n[j][i] - n[2][i - 1]));
}
}
}
int main()
{
unn N;
scanf("%lld", &N);
for (unn i = 0; i < 3; i++)
{
for (unn j = 0; j < N; j++)
{
scanf("%lld", &(n[i][j]));
}
}
toDiff(N);
printf("%lldn", min(df[0][N - 1], df[1][N - 1], df[2][N - 1]));
return 0;
}
D : 最长上升子序列
题目描述
对于一个整数序列 A =(a_1, a_2,ldots , a_k)A=(a
1
,a
2
,…,a
k
),定义 AA 的子序列为:从 AA 中删除若干个元素后(允许不删,也允许将所有 kk 个元素都删除),剩下的元素按照原来的顺序所组成的序列。如果这个子序列的元素从左到右严格递增,则称它为 AA 的一个上升子序列。其中包含元素数量最多的上升子序列称为 AA 的最长上升子序列。例如,(2, 4, 5, 6)(2,4,5,6) 和 (1, 4, 5, 6)(1,4,5,6) 都是 (2, 1, 1, 4, 7, 5, 6)(2,1,1,4,7,5,6) 的最长上升子序列,长度都为 44。
那么,给定一个序列 A =(a_1, a_2,ldots , a_n)A=(a
1
,a
2
,…,a
n
), 求 AA 的最长上升子序列的长度
输入格式
第一行一个整数 nn ,代表序列 AA 的长度
第二行是 nn 个由空格隔开的整数,代表 a_1, a_2,ldots , a_na
1
,a
2
,…,a
n
输出格式
一个整数,代表最长上升子序列的长度
测试样例
样例输入
7
2 1 1 4 7 5 6
样例输出
4
数据规模
对于 $100% $ 的数据,1 le n le 10^6, 1le a_i le10^61≤n≤10
6
,1≤a
i
≤10
6
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#define MOD 998244353
#define MAXN 1000010
#define INF 0x3f3f3f3f
using namespace std;
vector<int> a;
int len = 0;
int dp[MAXN];//记录长度为i的上升子串末尾元素的最小值
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int temp;
cin >> temp;
if (dp[len] < temp) dp[++len] = temp;
else {
int j = lower_bound(dp, dp + len,temp)-dp;
dp[j] = temp;
}
}
cout << len << endl;
return 0;
}
E : 最长公共子序列
题目描述
给定序列 A =(a_1, a_2,ldots , a_n)A=(a
1
,a
2
,…,a
n
) 和 B =(b_1, b_2,ldots , b_m)B=(b
1
,b
2
,…,b
m
) ,求它们的最长公共子序列
子序列的定义参考题目 最长上升子序列
输入格式
第一行两个整数 n, mn,m 代表序列 A,BA,B 的长度
第二行是 nn 个由空格隔开的整数,代表 a_1, a_2,ldots , a_na
1
,a
2
,…,a
n
第三行是 mm 个由空格隔开的整数,代表 b_1,b_2,ldots , b_mb
1
,b
2
,…,b
m
输出格式
输出一个整数,代表最长公共子序列的长度
测试样例
样例输入
5 5
3 2 1 4 5
1 2 3 4 5
样例输出
3
数据规模
对于 100 %100% 的数据,1 le n, m le 5000, 1le a_i, b_ile10^41≤n,m≤5000,1≤a
i
,b
i
≤10
4
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#define MOD 998244353
#define MAXN 1000010
#define INF 0x3f3f3f3f
using namespace std;
int dp[5010][5010];
int a[5010];
int b[5010];
int main() {
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < m; i++) {
cin >> b[i];
}
//定义边界
for (int i = 0; i < n; i++) {
dp[i][0] = 0;
}
for (int i = 0; i < m; i++) {
dp[0][i] = 0;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i - 1] == b[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[n][m] << endl;
return 0;
}
思路
A
动态规划;
到达第n阶台阶可以由n-1,n-2,…,n-k到达;
即f[n]=f[n-1]+f[n-2]+…+f[n-k]。递归求解即可(求解时可以储存,防止重复求解费时间)
B拿数问题
本题中,有三个数组,a数组用来记录输入的序列,A数组用来记录每个值出现的次数,对应的值就是它的编号,score数组用来记录每一步的最大分数值。
在输入a数组时,每输入一个值,A数组对应位置加一,表示出现次数。
之后用sort对a数组进行升序排序,目的是为了找到所有值的最大值和最小值。
之后遍历每个值,用score[i]=max(score[i-1],score[i-2]+iA[i]),这个式子计算以这个值为最大值能取到的最大分数为多少。
(以i为最大值取得的最大分数为,以i-1为最大值取得的最大分数,或者以i-2为最大值取得的最大分数+ii的出现次数,这两者取最大值)
最后在所有以某个值为最大值取得的最大分数中,找到最大的一个,就是答案。
C矩阵
采用动态规划;
重载max、min函数;
维护df数组,df[j][i] = min(df[0][i - 1] + abs(n[j][i] - n[0][i - 1]),df[1][i - 1] + abs(n[j][i] - n[1][i - 1]),df[2][i - 1] + abs(n[j][i] - n[2][i - 1]));
最后输出min(df[0][N - 1], df[1][N - 1], df[2][N - 1])即可
D
模拟一个单调栈
每遇到一个比栈顶元素大的数,就放进栈里,遇到比栈顶元素小的就二分查找前边的元素,找到一个“最应该被换掉的元素”,用新数去更新前边的元素。这个元素可能不是最优解的一部分,但是它可以使得后面还未加入的、比较小的数更有可能进入这个队列。
E最长公共子序列
最长公共子序列(LCS):就是A和B的公共子序列中长度最长的(包含元素最多的)
仍然用序列1,3,5,4,2,6,8,7和序列1,4,8,6,7,5为例,它们的最长公共子序列有1,4,8,7和1,4,6,7两种,但最长公共子序列的长度是4。由此可见,最长公共子序列(LCS)也不一定唯一。
使用动态规划算法!
我们用Ax表示序列A的连续前x项构成的子序列,即Ax= a1,a2,……ax, By= b1,b2,……by, 我们用LCS(x, y)表示它们的最长公共子序列长度,那原问题等价于求LCS(m,n)。为了方便我们用L(x, y)表示Ax和By的一个最长公共子序列。让我们来看看如何求LCS(x, y)。我们令x表示子序列考虑最后一项
(1) Ax = By
那么它们L(Ax, By)的最后一项一定是这个元素
(2) Ax ≠ By
仍然设t = L(Ax, By), 或者L(Ax, By)是空序列(这时t是未定义值不等于任何值)。则t ≠ Ax和t ≠ By至少有一个成立,因为t不能同时等于两个不同的值
这个博客讲解很详细
最后
以上就是刻苦西牛为你收集整理的Week11-动态规划(一)A : 爬台阶B : 拿数问题C : 矩阵选数D : 最长上升子序列E : 最长公共子序列思路的全部内容,希望文章能够帮你解决Week11-动态规划(一)A : 爬台阶B : 拿数问题C : 矩阵选数D : 最长上升子序列E : 最长公共子序列思路所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复