算法竞赛必考算法——动态规划(01背包和完全背包)

动态规划(一)

在这里插入图片描述

目录

  • 动态规划(一)
    • 1.01背包问题
      • 1.1题目介绍
      • 1.2思路一介绍(二维数组)
      • 1.3思路二介绍(一维数组) ==空间优化==
      • 1.4思路三介绍(输入数据优化)
    • 2.完全背包问题
      • 2.1题目描述:
      • 2.2思路一(朴素算法)
      • 2.3思路二(将k优化处理掉)
      • 2.4思路三(优化j的初始条件)
  • 总结

在这里插入图片描述

1.01背包问题

1.1题目介绍

在这里插入图片描述

1.2思路一介绍(二维数组)

在这里插入图片描述

代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
 const int N=1010;
 int v[N],w[N]; //v[N]是物品体积 w[N]是物品的价值
 int f[N][N]; //f[i][j]在体积不超j的前提下,从i个物品中选择最大值
 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=1;j<=m;j++)
         {
             f[i][j]=f[i-1][j];
             if(j>=v[i])//如果我们的背包容量j大于第i个物品的体积,我们在进行决策是否加入第i个物品
             f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
         }
     }
     cout<<f[n][m]<<endl;
     return 0;
 }

1.3思路二介绍(一维数组) 空间优化

  为什么可以使用一维数组?

  我们先来看一看01背包问题的状态转移方程,我们可以发现 f[i]只用到了f[i-1],其他的是没有用到的,我们可以用滚动数组来做。
  还有一个原因就是我们在计算f[i] [j]的时候我们只用到了f[i-1] [j]和f[i-1] [j-v[i]],其中j和j-v[i]都是小于等于j的,在j的同一侧,所以我们综合以上两点原因就可以将二维优化到一维,即完成空间优化。

在这里插入图片描述

我们先来将二维和优化后的一维在关键算法代码上进行一下对比:

for(int i = 1; i <= n; i++) 
    for(int j = m; j >= 0; j--)
    {
        if(j < v[i]) 
            f[i][j] = f[i - 1][j];  // 优化前
            f[j] = f[j];            // 优化后,该行自动成立,可省略。
        else    
            f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);  // 优化前
            f[j] = max(f[j], f[j - v[i]] + w[i]);                   // 优化后
    }

实际上,只有当枚举的背包容量 >= v[i] 时才会更新状态,因此我们可以修改循环终止条件进一步优化。

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

在这里插入图片描述

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

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

int main()
{
    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;
}

1.4思路三介绍(输入数据优化)

  我们注意到在处理数据时,我们是一个物品一个物品,一个一个体积的枚举。因此我们可以不必开两个数组记录体积和价值,而是边输入边处理。

#include<bits/stdc++.h>

using namespace std;

const int MAXN = 1005;
int f[MAXN];  // 

int main() 
{
    int n, m;   
    cin >> n >> m;

    for(int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;      // 边输入边处理
        for(int j = m; j >= v; j--)
            f[j] = max(f[j], f[j - v] + w);
    }

    cout << f[m] << endl;

    return 0;
}

在这里插入图片描述

2.完全背包问题

2.1题目描述:

在这里插入图片描述
在这里插入图片描述

2.2思路一(朴素算法)

#include<iostream>
using namespace std;
const int N=1010;
int f[N][N];
int n,m;
int v[N],w[N];
int main()
{
    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++)
        {
            for(int k=0;k*v[i]<=j;k++)
            {
                f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
            }
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

上述朴素算法和01背包问题的朴素算法非常相似,但是会超时。所以我们接下来就会对这个算法进行优化处理。

2.3思路二(将k优化处理掉)

在这里插入图片描述

  我们先来分析一下状态转移方程,我们发现方程一和方程二有一定的联系,我们先不看方程一红色圈出来的部分,我们比较方程一黄色的部分和方程二的内容,我们发现方程一就是比方程二每一项多了一个w,那么我们黄色圈出来的部分的最大值也就比方程二的最大值多w,那么我们其实就可以将方程一圈出来黄色的部分进行等价替换,替换成红色方框黄色字体的内容,我们最终得出最下方的结论,其实我们要求得最大值之和两个状态有关,比较它们的最大值即可。

我们发现好像最后的状态转移方程和k并没有关系了,那么我们就干脆去掉k的那次循环

所以我们对核心代码进行了优化:

for(int i = 1 ; i <=n ;i++)
for(int j = 0 ; j <=m ;j++)
{
    f[i][j] = f[i-1][j];//状态一,即不取第i个物品
    if(j-v[i]>=0)//判断是否可以加入第i个物品
        f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);//状态二
}

完整代码如下:

#include<iostream>
using namespace std;
const int N=1010;
int f[N][N];
int n,m;
int v[N],w[N];
int main()
{
    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(j>=v[i])
            {
                f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
            }
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

我们来比较一个完全背包的核心代码和01背包核心代码的区别:

//01背包问题核心优化后代码
for(int i=1;i<=n;i++)
{
	for(int j=m;j>=v[i];j--)
	{	f[i][j]=f[i-1][j];
		if(j>=v[i])
		{
			f[i][j]=max(f[i][j],f[i-1][j-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];//状态一,即不取第i个物品
    if(j-v[i]>=0)//判断是否可以加入第i个物品
        f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);//状态二
}

我们发现其实本质也就是一句不同:注意i的下标

在这里插入图片描述

  我们这个i的下标是根据两个不同的背包问题的状态转移方程得出来的,我们01背包问题因为要使用第i-1层的数据,所以我们枚举j的时候只能从后往前枚举,这样做是因为j-v[i]小于j,那么f[j-v[i]]的数据就会被改,那么我们使用的数据其实就是第i层的数据了,不满足状态转移方程,所以我们要从后往前枚举,但是完全背包问题使用的就是第i层的数据,所以不存在从前往后枚举就会在使用前数据就发生意外改变的这种情况,所以就在这个地方这两个核心算法略有差别。

2.4思路三(优化j的初始条件)

这个和01背包问题的优化方法是一样的,就不多赘述了。

核心代码如下:

for(int i = 1 ; i<=n ;i++)
    for(int j = v[i] ; j<=m ;j++)//注意了,这里的j是从小到大枚举,和01背包不一样
    {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
    }

完整代码如下:

#include<iostream>
using namespace std;
const int N = 1010;
int f[N];
int v[N],w[N];
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 = v[i] ; j<=m ;j++)
    {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
    }
    cout<<f[m]<<endl;
}

总结

  本篇博客主要涉及动态规划背包问题的01背包问题和完全背包问题,给大家分享了实现的思路和代码模板,大家也可以看看yxc的背包九讲,图片转载于yxc,希望对大家有所帮助,后面会持续更新~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/1154.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud Alibaba全家桶(四)——微服务调用组件Feign

前言 本文小新为大家带来 微服务调用组件Feign 的相关知识&#xff0c;具体内容包含什么是Feign&#xff0c;Spring Cloud Alibaba快速整合OpenFeign&#xff0c;Spring Cloud Feign的自定义配置及使用&#xff08;包括&#xff1a;日志配置、契约配置、自定义拦截器实现认证逻…

Autosar-ComM浅谈

文章目录 一、ComM概述二、和其他模块的依赖关系三、ComM通道状态机ComM模式与通讯能力关系表四、ComM中的PNC一、ComM概述 ComM全称是Communication Manager,顾名思义就是通信的管理,是BSW(基本软件)服务层的一个组件。 ComM的作用: 为用户简化Communication Stack的使用…

中断控制器

在Linux内核中&#xff0c;各个设备驱动可以简单地调用request_irq&#xff08;&#xff09;、enable_irq&#xff08;&#xff09;、disable_irq&#xff08;&#xff09;、 local_irq_disable&#xff08;&#xff09;、local_irq_enable&#xff08;&#xff09;等通用API来…

STM32----MPU6050

前言&#xff1a;最近几个月没有写文章了&#xff0c;因为这学期的事情真的有点多&#xff0c;但是想了想&#xff0c;文章还是要更新&#xff0c;总结自己学习的知识&#xff0c;真的很重要&#xff01;&#xff01;&#xff01; 废话不多说&#xff0c;正文开始&#xff1a;…

【vue.js】在网页中实现一个金属抛光质感的按钮

文章目录前言效果电脑效果手机效果说明完整代码index.html前言 诶&#xff1f;这有一个按钮(&#xff5e;&#xffe3;▽&#xffe3;)&#xff5e;&#xff0c;这是一个在html中实现的具有金属质感并且能镜面反射的按钮~ 效果 电脑效果 手机效果 说明 主要思路是使用 navig…

【算法基础】二分图(染色法 匈牙利算法)

一、二分图 1. 染色法 一个图是二分图,当且仅当,图中不含奇数环。在判别一个图是否为二分图⑩,其实相当于染色问题,每条边的两个点必须是不同的颜色,一共有两种颜色,如果染色过程中出现矛盾,则说明不是二分图。 for i = 1 to n:if i 未染色DFS(i, 1); //将i号点染色未…

Leetcode138. 复制带随机指针的链表

复制带随机指针的链表 第一步 拷贝节点链接在原节点的后面 第二步拷贝原节点的random &#xff0c; 拷贝节点的 random 在原节点 random 的 next 第三步 将拷贝的节点尾插到一个新链表 ,并且将原链表恢复 从前往后遍历链表 ,将原链表的每个节点进行复制&#xff0c;并l链接到原…

【STL二】STL序列式容器(array、vector、deque、list、forward_list)

【STL二】STL序列式容器&#xff08;array、vector、deque、list、forward_list&#xff09;1.array<T,N>&#xff08;数组容器&#xff09;2.vector<T>&#xff08;向量容器&#xff09;3.deque<T>&#xff08;双端队列容器&#xff09;&#xff1a;4.list&…

第一个 Qt 程序

第一个 Qt 程序 “hello world ”的起源要追溯到 1972 年&#xff0c;贝尔实验室著名研究员 Brian Kernighan 在撰写 “B 语言教程与指导(Tutorial Introduction to the Language B)”时初次使用&#xff08;程序&#xff09;&#xff0c;这是目前已 知最早的在计算机著作中将…

用sql计算两个经纬度坐标距离(米数互转)

目录 一、sql示例&#xff08;由近到远&#xff09; 二 、参数讲解 三、查询效果 - 距离&#xff08;公里 / 千米&#xff09; 四、查询效果 - 距离&#xff08;米&#xff09; 五、距离四舍五入保留后2位小数&#xff08;java&#xff09; 一、sql示例&#xff08;由近到远…

2023年最新最全 VSCode 插件推荐

Visual Studio Code 是由微软开发的一款免费的、针对于编写现代Web和云应用的跨平台源代码编辑器。它包含了一个丰富的插件市场&#xff0c;提供了很多实用的插件。下面就来分享 2023 年前端必备的 VS Code 插件&#xff01; 前端框架 ES7 React/Redux/React-Native snippets …

【OpenCV】车牌自动识别算法的设计与实现

写目录一. &#x1f981; 设计任务说明1.1 主要设计内容1.1.1 设计并实现车牌自动识别算法&#xff0c;基本功能要求1.1.2 参考资料1.1.3 参考界面布局1.2 开发该系统软件环境及使用的技术说明1.3 开发计划二. &#x1f981; 系统设计2.1 功能分析2.1.1 车辆图像获取2.1.2 车牌…

渗透测试靶机vulnhub——DC3实战笔记

vm在导入虚拟机的时候把IDE里面的改成IDE 0:0信息收集fscan扫描存活主机目标机器是192.168.1.106nmap扫描端口nmap -A 192.168.1.106 -p- …

Linux中sudo,su与su -命令的区别

前言 su命令就是切换用户的工具&#xff0c;怎么理解呢&#xff1f;比如我们以普通用户tom登录的&#xff0c;但要添加用户任务&#xff0c;执行useradd &#xff0c;tom用户没有这个权限&#xff0c;而这个权限恰恰由root所拥有。解决办法无法有两个&#xff0c;一是退出tom用…

【AI 工具】文心一言内测记录

文章目录一、申请内测二、收到内测邀请三、激活内测四、开始使用1、普通对话2、生成图片3、生成代码4、写剧本5、生成小说五、问题反馈一、申请内测 到 https://yiyan.baidu.com/welcome 页面 , 点击 " 开始体验 " 按钮 , 申请试用 ; 申请时 , 需要填写相关信息 ; 主…

关于.Net和Java的看法——我见过最牛的一个小实习生经历

1、背景 笔者&#xff08;小方同学在学习&#xff09;是一个专科院校的一名普通学生&#xff0c;目前就职于某三线城市的WEB方面.Net开发实习生&#xff0c;在找实习期间和就业期间的一些看法&#xff0c;发表此文&#xff0c;纯个人想法&#xff0c;欢迎讨论&#xff0c;指正…

JavaWeb《一》概念、服务器部署及servlet

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; 本文是javaweb的第一篇&#xff0c;首先介绍了javaweb&#xff0c;然后进行了一个简单的web服务器部署&#xff0c;把我的一个网页发布到了云端&#xff0c;且叫他小Sa&#xff0c;目前啥也没有&#xff0c;之后会使…

【数据结构】万字深入浅出讲解单链表(附原码 | 超详解)

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;C语言实现数据结构 &#x1f4ac;总结&#xff1a;希望你看完…

进程和线程的区别和联系

进程和线程的区别和联系1. 认识线程2. 进程和线程的关系3. 进程和线程的区别4. 线程共享了进程哪些资源1. 上下文切换2. 线程共享了进程哪些资源1.代码区2. 数据区3. 堆区1. 认识线程 线程是进程的一个实体,它被包含在进程中,一个进程至少包含一个线程,一个进程也可以包含多个…

Python数据分析案例22——财经新闻可信度分析(线性回归,主成分回归,随机森林回归)

本次案例还是适合人文社科领域&#xff0c;金融或者新闻专业。本科生做线性回归和主成分回归就够了&#xff0c;研究生还可以加随机森林回归&#xff0c;其方法足够人文社科领域的硕士毕业论文了。 案例背景 有八个自变量&#xff0c;[微博平台可信度,专业性,可信赖性,转发量,…