Week11-动态规划(一)

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("%lld\n", 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("%lld\n", 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("%lld\n", 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, 1\le 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, 1\le a_i, b_i\le10^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不能同时等于两个不同的值
这个博客讲解很详细

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!对于心脏病预测的问题,使用循环神经网络(RNN)是一种常见的方法。RNN适用于处理序列数据,而心电图信号就是一种序列数据。在使用RNN进行心脏病预测时,你可以将心电图信号作为输入序列,然后通过训练RNN模型来预测患者是否患有心脏病。 首先,你需要准备一个合适的数据集,其中包含心电图信号和相应的心脏病标签。可以使用公开的心电图数据集,如PTB数据库或MIT-BIH数据库。然后,你可以对数据进行预处理和特征工程,如数据清洗、滤波、降采样等。 接下来,你可以构建一个RNN模型。RNN模型由一系列循环层组成,每个循环层都会处理一个时间步的输入数据。你可以选择不同类型的RNN单元,如简单循环单元(SimpleRNN)、长短期记忆网络(LSTM)或门控循环单元(GRU)。通过添加适当的全连接层和激活函数,你可以将RNN模型输出映射到二分类问题(有或无心脏病)的结果。 然后,你可以使用训练集对RNN模型进行训练,并使用验证集进行模型调优。在训练过程中,你可以使用适当的损失函数(如交叉熵)和优化算法(如随机梯度下降)来最小化模型的预测误差。 最后,你可以使用测试集对训练好的模型进行评估,并计算模型的性能指标,如准确率、精确率、召回率等。这些指标可以帮助你评估模型的预测能力和泛化能力。 需要注意的是,心脏病预测是一个复杂的医学问题,仅仅使用心电图信号可能不能得到准确的预测结果。通常情况下,还需要结合其他患者的临床信息和医学知识来进行综合评估。因此,在进行心脏病预测时,建议与专业医生合作,并遵循相关的医学准则和规范。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值