蓝桥杯第六讲--简单dp【例题】

这篇具有很好参考价值的文章主要介绍了蓝桥杯第六讲--简单dp【例题】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

蓝桥杯官网:蓝桥杯大赛——全国大学生TMT行业赛事
✨本博客讲解 蓝桥杯C/C++ 备赛所涉及算法知识,此博客为第六讲:简单dp【例题】

本篇博客所包含习题有:
👊01背包问题
👊摘花生
👊最长上升子序列

简单dp【习题】见博客:蓝桥杯第六讲–简单dp【习题】

博客内容以题代讲,通过讲解题目的做法来帮助读者快速理解算法内容,需要注意:学习算法不能光过脑,更要实践,请读者务必自己敲写一遍本博客相关代码!!!


01背包问题

题目要求

题目描述:

N N N 件物品和一个容量是 V V V 的背包。每件物品只能使用一次。

i i i 件物品的体积是 v i v_i vi,价值是 w i w_i wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式:

第一行两个整数, N , V N,V NV,用空格隔开,分别表示物品数量和背包容积。

接下来有 N N N 行,每行两个整数 v i , w i v_i,w_i vi,wi,用空格隔开,分别表示第 i i i 件物品的体积和价值。

输出格式:

输出一个整数,表示最大价值。

数据范围:

0 < N , V ≤ 1000 0<N,V≤1000 0<N,V1000
0 < v i , w i ≤ 1000 0<v_i,w_i≤1000 0<vi,wi1000

输入样例:

4 5
1 2
2 4
3 4
4 5

输出样例:

8

思路分析

朴素版

蓝桥杯第六讲--简单dp【例题】
上图其实已经说的很详细了,用文字去解释就是我们定义的状态: f [ i ] [ j ] f[i][j] f[i][j],就是表示从前 i i i 个物品里面进行选择,选择的总体积不超过 j j j 的最大值,我们的状态转移方程推导:对于第 i i i 个商品,我们有两种决策:选它或者不选它,如果不选择它,对应此时的状态就是 f [ i − 1 ] [ j ] f[i - 1][j] f[i1][j],如果选择它,对应此时的状态就是 f [ i − 1 ] [ j − v [ i ] ] + w [ i ] f[i - 1][j - v[i]] + w[i] f[i1][jv[i]]+w[i],每次决策都取两者的最大值。

优化

通过朴素版的分析,可以明白:对于第一位状态,我们其实只会用到它的上一维状态去进行优化,如我们在计算 f [ i ] [ j ] f[i][j] f[i][j] 的时候,只会用到 f [ i − 1 ] f[i - 1] f[i1],对于 f [ i − 2 ] f[i - 2] f[i2] 这种状态我们是不会使用的,所以我们可以把第一维给去掉,那么代码1:

    for (int i = 1; i <= n; i ++ )
       for (int j = 0; j <= m; j ++ ) 
       {
           f[i][j] = f[i - 1][j];
           if (v[i] <= j) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
       }

就会变成代码2:

    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ ) 
        {
            f[j] = f[j];
            if (v[i] <= j) f[j] = max(f[j], f[j - v[i]] + w[i]);
        }

我们观察到:f[j] = f[j];是一个恒等式,故可以删掉,变成代码3:

    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ ) 
        {
            if (v[i] <= j) f[j] = max(f[j], f[j - v[i]] + w[i]);
        }

我们来对比一下代码1代码3,发现它们两个其实是由本质的区别的,我们在代码1中的装填转移方程中所用到的f[i - 1][j - v[i]] + w[i]是第 i − 1 i - 1 i1 层的变量,但是在代码3中,由于我们的 j j j 是从小到大进行枚举的,且 j − v [ i ] j - v[i] jv[i] 肯定是小于 j j j 的,所以我们在计算 f [ j ] f[j] f[j] 的时候(第 i i i 层),此时我们的 f [ j − v [ i ] ] f[j - v[i]] f[jv[i]] 已经被计算过(第 i i i 层),故代码3中的f[j - v[i]] + w[i]其实是属于第 i i i 层的,这样显然是错误的,我们可以采用一种取巧的方法:第二层 f o r for for 循环从大到小进行更新,这时,第 i i i 层的 f [ j ] f[j] f[j] 在计算的时候,由于 j − v [ i ] j - v[i] jv[i] 肯定小于 j j j,即此时的 f [ j − v [ i ] ] f[j - v[i]] f[jv[i]] 其实还是第 i − 1 i - 1 i1 层,这样就和代码1是保持一致的了。

代码(朴素版)

#include <iostream>
#include <cstring>
#include <algorithm>

const int N = 1010;

int v[N], w[N];
int f[N][N];

using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) 
        cin >> v[i] >> w[i];

    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ ) 
        {
            f[i][j] = f[i - 1][j];
            if (v[i] <= j) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
        }

    cout << f[n][m] << endl;

    return 0;
}

代码(优化)

#include <iostream>
#include <cstring>
#include <algorithm>

const int N = 1010;

int v[N], w[N];
int f[N];

using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) 
        cin >> v[i] >> w[i];

    for (int i = 1; i <= n; i ++ )
        for (int j = m; j >= v[i]; j -- ) 
            f[j] = max(f[j], f[j - v[i]] + w[i]);

    cout << f[m] << endl;

    return 0;
}

摘花生

题目要求

题目描述:

Hello Kitty 想摘点花生送给她喜欢的米老鼠。

她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。

地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。

Hello Kitty 只能向东或向南走,不能向西或向北走。

问 Hello Kitty 最多能够摘到多少颗花生。
蓝桥杯第六讲--简单dp【例题】

输入格式:

第一行是一个整数 T T T,代表一共有多少组数据。

接下来是 T T T 组数据。

每组数据的第一行是两个整数,分别代表花生苗的行数 R R R 和列数 C C C

每组数据的接下来 R R R 行数据,从北向南依次描述每行花生苗的情况。每行数据有 C C C 个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目 M M M

输出格式:

对每组输入数据,输出一行,内容为 Hello Kitty 能摘到得最多的花生颗数。

数据范围:

1 ≤ T ≤ 100 , 1≤T≤100, 1T100,
1 ≤ R , C ≤ 100 , 1≤R,C≤100, 1R,C100,
0 ≤ M ≤ 1000 0≤M≤1000 0M1000

输入样例:

2
2 2
1 1
3 4
2 3
2 3 4
1 6 5

输出样例:

8
16

思路分析

拿到一个题目后,我们分为两步去考虑这个问题:
第一步: 把题目进行抽象:其实就是对于一个矩形,我们从它的左上角走到右下角,每一个点上都一个数值,走到一个点后就加上这个点的值,每次只能往右走或者往下走,问走到右下角的话,数值的累计最大值是多少。
第二步: 进行 d p dp dp分析:比如我们现在在 [ i , j ] [i, j] [i,j]这个点上, d p dp dp的关键就是分析这个点可以由哪个点转换而来:显然,对于这个点而言,根据题意可以由上面的一个点转换,亦可由左边的一个点转换过来,而我们的需求为最大值,故我们可以得到状态转移方程:f[i][j] = max(f[i - 1][j], f[i][j - 1]) + w[i][j];

代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 110;

int w[N][N];
int f[N][N];

int main()
{
    int t;
    cin >> t;
    
    while (t -- ) 
    {
        int r, c;
        cin >> r >> c;
        for (int i = 1; i <= r; i ++ ) 
            for (int j = 1; j <= c; j ++ ) 
                cin >> w[i][j];
                
        for (int i = 1; i <= r; i ++ ) 
            for (int j = 1; j <= c; j ++ )
                f[i][j] = max(f[i - 1][j], f[i][j - 1]) + w[i][j];
                
        cout << f[r][c] << endl;
    }
    
    return 0;
}

最长上升子序列

题目要求

题目描述:

给定一个长度为 N N N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式:

第一行包含整数 N N N

第二行包含 N N N 个整数,表示完整序列。

输出格式:

输出一个整数,表示最大长度。

数据范围:

1 ≤ N ≤ 1000 , 1≤N≤1000, 1N1000
− 1 0 9 ≤ −10^9≤ 109 数列中的数 ≤ 1 0 9 ≤10^9 109

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

思路分析

蓝桥杯第六讲--简单dp【例题】
我们要求的是最长上升子序列,状态表示 f [ i ] f[i] f[i] 表示的是在以第 i i i 个数为结尾的最长上升子序列的长度,所以我们的状态转移就是去枚举 j j j j j j 的范围是 [ 1 , i − 1 ] [1, i - 1] [1,i1],如果有 a [ j ] < a [ i ] a[j] < a[i] a[j]<a[i],那么我们就取一个最大值:f[i] = max(f[i], f[j] + 1);,注意,由于我们的状态定义,故 f [ i ] f[i] f[i] 的最小值是 1 1 1(它自己单独为一个最长上升子序列),最后的结果需要重新遍历一遍,取得所有 f [ i ] f[i] f[i] 的最大值。文章来源地址https://www.toymoban.com/news/detail-424501.html

代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1010;

int a[N];
int f[N];

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i ++ )
        cin >> a[i];
        
    for (int i = 1; i <= n; i ++ )
    {
        f[i] = 1;
        for (int j = 1; j < i; j ++ ) 
            if (a[j] < a[i])
                f[i] = max(f[i], f[j] + 1);
    }
    
    int res = 0;
    for (int i = 1; i <= n; i ++ ) 
        res = max(res, f[i]);
    
    cout << res << endl;
    
    return 0;
}

到了这里,关于蓝桥杯第六讲--简单dp【例题】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 蓝桥杯第14天(Python版)

    并查集的使用 二分查找 关于二分法的两个模板 找=x的第一个,mid=(low+high)//2 找=x的第一个,mid=(low+high+1)//2 一元三次方程求解 #一半测试案例错误 已改正,注意学会使用continue语句 标注答案(我感觉没判断f(100)处) 求立方根问题 学会善用break语句,continue语句 之前写

    2023年04月09日
    浏览(30)
  • 第六讲:静态路由的原理和配置

    首先我们知道路由器是工作在网络层的,那就是三层设备。网络层的功能主要为:不同网段之间通信、最佳路径选择也就是逻辑地址(ip地址)寻址、转发数据。 ●路由器是能将数据包转发到正确的目的地,并且在转发过程中选择最佳路径的设备,用于不同网络之间的通信。

    2024年02月05日
    浏览(42)
  • Redis 7 第六讲 主从模式(replica)

    🌹🌹🌹   此篇开始进入架构篇范围(❤´艸`❤)          即主从复制,master以写为主,Slave以读为主。当master数据变化的时候,自动将新的数据异步同步到其它slave数据库。 读写分离 容灾备份 数据备份 水平扩容 参数名 默认值 修改值 6379 6380 6381 daemo

    2024年02月10日
    浏览(36)
  • 蓝桥杯第十三届决赛真题-左移右移

    题目链接 问题描述 小蓝有一个长度为 N 的数组, 初始时从左到右依次是 1,2,3, …N 。 之后小蓝对这个数组进行了 M 次操作, 每次操作可能是以下 2 种之一: 左移 x, 即把 x 移动到最左边。 右移 x, 即把 x 移动到最右边。 请你回答经过 M 次操作之后, 数组从左到右每个数是多少? 输

    2023年04月09日
    浏览(43)
  • 蓝桥杯第20天(Python)(疯狂刷题第3天)

    1.思维题/杂题:数学公式,分析题意,找规律 2.BFS/DFS:广搜(递归实现),深搜(deque实现) 3.简单数论:模,素数(只需要判断到 int(sqrt(n))+1),gcd,lcm,快速幂(位运算移位操作),大数分解(分解为质数的乘积) 4.简单图论:最短路(一对多(Dijstra,临接表,矩

    2023年04月22日
    浏览(32)
  • 蓝桥杯第19天(Python)(疯狂刷题第2天)

    1.思维题/杂题:数学公式,分析题意,找规律 2.BFS/DFS:广搜(递归实现),深搜(deque实现) 3.简单数论:模,素数(只需要判断到 int(sqrt(n))+1),gcd,lcm,快速幂(位运算移位操作),大数分解(分解为质数的乘积) 4.简单图论:最短路(一对多(Dijstra,临接表,矩

    2023年04月22日
    浏览(32)
  • 《互联网的世界》第六讲-去中心化和安全

    互联网构建于开放互联的中立原则之上,公平接入,数据互联互通,流量被无差别对待,这意味着互联网本质上是匿名,去中心的,这与我们的现实世界完全不同。 但互联网上的主流业务却是 c/s 产销模式,试图在互联网世界复刻现实世界。我们对比开放互联的中立原则和现

    2024年03月19日
    浏览(87)
  • 蓝桥杯第十三届单片机国赛程序

    写在前面: 做完总体来说感觉一年比一年难了(估计是被骂的),虽然十三届用的底层少,但是做起来困难重重。 最难的难点在于定时器安排问题。15F2K60S2系列单片机只有三个定时器,本届题目考到了频率测量、超声波、PWM输出,再加上刷新,每一个都需要一个定时器,比

    2024年02月08日
    浏览(49)
  • 第六讲 Java面向对象-Java中的异常 (头歌答案)

    目录 第六讲  内部类  异常处理 第1关:Java 中的异常处理机制 第2关:捕获异常                源码 第3关:抛出异常               源码: 第4关:自定义异常               源码   (一)什么是异常 异常:程序在运行过程中产生的不正常情况。 一些不被预期的事件

    2024年02月05日
    浏览(40)
  • 【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题,真题分析与代码讲解

    🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏 🎏【蓝桥杯嵌入式】蓝桥杯第十届省赛真题 🎏【蓝桥杯嵌入式】蓝桥杯第十三届省赛程序真题 🪔本系列专栏 -  

    2023年04月15日
    浏览(69)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包