第五章——动态规划1

news2024/9/21 6:04:56

背包问题

01背包问题

有N个·物品和容量是V的背包,每个物品有价值vi和权重(价值)wi属性,每件物品只能用一次(要么用0次,要么用1次),在背包能装得下的情况下,挑一部分物品装入背包,怎样挑才能保证这些物品的价值最大。

f(i,j)集合表示所有选法里面的最大值

f(N,V)表示从前N个物品选,总体积不超过V的集合,状态计算表示的是集合的划分,状态计算是把当前的集合表示成更小的子集,即把当前状态用前面更小的状态表示出来,我们这里采用把f(i,j)所有的选法表示成俩大类,以含i和不含i为划分标准。

划分原则:

  1. 这里划分的时候要做到不重复,即某一个元素不能属于俩个集合,只能属于其中一个。

  1. 不漏,不能漏掉任何一个小的元素(不能让某个元素不属于任何一个集合)

  1. 不漏一定要满足,但是不重有时候可以不用满足。

左边的集合的特点是从1到i-1中选,并且总体积不超过j。用f(i-1.j)表示.

右边集合的特点是所有从1到i中选,总体积不超过j,可以包含i。

步骤:

  1. 右边的选法里面都包含第i个物品,我们在选择的时候先把每种选法里面的第i个物品都去掉,这个不会影响最大值。

  1. 右边也变成了从i到i-1中选,由于没去掉第i个物品时,总体积不超过j,去掉了第i个物品后,总体积不超过j-vi。右边变成f(i-1,j-vi)

  1. 选完之后,我们把第i个物品再加回来,右边这种情况的最大价值是f(i-1,j-vi)+wi

  1. 我们最后将左边和右边价值相比较,取最大值即可

  1. 注意左边集合的这种情况是一定存在的,右边这种情况不一定存在,可能会是空集,当背包体积小于右边vi的时候就是空集。

二维表示

#include<iostream>
#include<algorithm>
using namespace std;
// 01背包问题
const int N = 1010;
int n, m;//n表示物品个数,m表示背包容量
int v[N], w[N];//V表示体积,w表示价值
int f[N][N];  //表示状态
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> v[i] >> w[i];
    //初始化的时候 要计算所有的状态,f[0~n][0~m]是所有的状态
    //f[0][0~m]表示0件物品,总体积不超过0,价值0~m,这样的情况下最大价值是多少,由于一件物品都没选,所以它是0,由于数组会默认是0,我们就不需要初始化
    //因此我们从1开始枚举
    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 - 1][j - v[i]] + w[i]);
        }
    }
    cout << f[n][m] << endl;
    return 0;
}

一维表示

int f[N];
const int N = 1010;
int n, m;//n表示物品个数,m表示背包容量
int v[N], w[N];//V表示体积,w表示价值
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> v[i] >> w[i];
    //初始化的时候 要计算所有的状态,f[0~n][0~m]是所有的状态
    //f[0][0~m]表示0件物品,总体积不超过0,价值0~m,这样的情况下最大价值是多少,由于一件物品都没选,所以它是0,由于数组会默认是0,我们就不需要初始化
    //因此我们从1开始枚举
    for (int i = 1; i <= n; ++i)
    {
        for (int j = m; j >= v[i]; j--)//一维数组表示的时候,j从0到vi-1的时候是没有意义的,因为下面有这个判断,
            //所以我们直接从m开始,然后这个判断也可以不要
            //从m开始的另一个原因,要保证出现跟f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);一样的效果
        {
            //if (j >= v[i])//右边这种情况不一定存在
                f[j] = max(f[j], f[j - v[i]] + w[i]);
                
        }
    }
    cout << f[m] << endl;
    return 0;
}

完全背包问题

每件物品有无限个。

状态计算和01背包问题有所不同,完全背包问题,状态计算表示的是集合的划分。

该集合表示的是第i个物品选0个,第i个物品选1个,第i个物品选2个,第i个物品选3个,第i个物品选4个……一直到k个,要注意背包的容量,这里最多选K个。

f[i,j]表示前i个物品,总体积不超过j。

0这个子集表示第i个物品不选。等价于只考虑选前i-1个物品,并且总体积不超过j。

用f[i-1,j]来表示。

我们现在考虑选前i个物品,并且第i个物品选了K个这样选法的集合。

步骤:

  1. 去掉k个物品i,即去掉k个第i个物品

  1. 求max,由于去掉了k个第i个物品,现在变成了max=f[i-1,j-k*v[i]]

  1. 再把去掉的加回来,即+k个物品i

  1. f[i-1,j-k*v[i]]+k*w[i]

f[i,j]=f[i-1,j-v[i]*k]+w[i]*k。

朴素算法

const int N = 1010;
int n, m;
int v[N],w[N];
int f[N][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 - v[i]*k] + k * w[i]);
    cout << f[n][m] << endl;
    return 0;
}

优化二维算法

把下列式子展开

橙色部分比红色部分的每一项都多了W

我们再枚举的时候,枚举俩个状态就可以了,第二个状态在体积满足条件的情况下才可以使用

const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][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;
}

优化一维算法

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=v[i];j<=m;j++)
            f[j] = max(f[j], f[j - v[i]] + w[i]);
    cout << f[m] << endl;
    return 0;
}

多重背包问题1

多重背包问题,每件物品的个数是有限制的。

跟完全背包相似,根据第i个物品选多少个,把第i个物品分成若干种类

状态转移方程跟完全背包的朴素算法一样

k从0,1,2一直到s[i]

const int N = 110;
int n, m;
int v[N], w[N], s[N];
int f[N][N];
int main()
{
    cin >> n >>m;
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i] >> s[i];
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= m; j++)
            for (int k = 0; k <= s[i] && k * v[i] <= j; k++)//注意这里的体积范围,总共需要的体积小于等于给定的体积
                f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k);
    cout << f[n][m] << endl;
    return 0;
}

多重背包问题2

跟多重背包问题1相比,数据范围变大了,如果直接按多重背包问题1去做,会超时。

我们发现f[i,j-v]后面多了一项。因此,我们不能直接用完全背包的优化方式,来优化这道题

这里采用二进制的优化方式。

假设某个物品有1023个,s=1023,我们不需要从0枚举到1023,我们可以先把若干个第i个物品打包在一块来考虑,1,2,4,8……512分别代表第一组,第二组,第三组有1,2,4个i物品,即把i物品打包成10组,最多从每组中选1个,然后从选出来的这10个中平凑出0-1023中的任何一个数。

当s=200,我们可以这样打包,最后一组是73,分组规则按照以2为底的质数进行增长,但所有组数加一起的和不能超过某个数的总个数。

第i个物品有s个,我们可以拆分成新的logs组新的物品,新的物品只能用一次。

步骤:

  1. 先把第i个物品拆分

  1. 对拆分出来的新的物品,做一遍01背包即可

时间复杂度为

const int N = 25000, M = 2010;
int n,m;
int v[N], w[N];
int f[N];
int main()
{
    cin >>n >>m;
    int cnt = 0;//表示所有新的物品
    for (int i = 1; i <= n; i++)
    {
        int a, b, s;//当前物品的体积,价值,个数
        cin >> a >> b >> s;
        int k = 1;//从1开始分
        while (k<=s)
        {
            cnt++;//新物品的编号++
            v[cnt] = a * k;//体积
            w[cnt] = b * k;//价值
            s -= k;
            k *= 2;

        }
        if (s > 0)//此时剩下一些需要补上
        {
            cnt++;
            v[cnt] = a * s;
            w[cnt] = b * s;
        }
    }
    n = cnt;//分组完成
    //做01背包问题即可
    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;
}

分组背包问题

枚举第i组物品,选哪个。

从左到右分别是第i组物品选第0个,第i组物品选第1个……第i组物品选第n个。

从第i组物品选第k个

const int N =110 ;
int n, m;
int v[N][N], w[N][N],s[N];//s存每一组的个数
int f[N];
int main()
{
    cin >> n >>m;
    for (int i = 1; i <= n; i++)
    {
        cin >> s[i];
        for (int j = 0; j < s[i]; j++)
            cin >> v[i][j] >> w[i][j];
    }
    for (int i = 1; i <= n; i++)
        for (int j = m; j >= 0; j--)//从前往后枚举每一组物品,从大到小枚举所有体积
            for (int k = 0; k < s[i]; k++)//枚举所有选择
                if (v[i][k] <= j)//如果第i组,第k件物品<=j的话,我们才能往下进行,即要注意体积大小
                    f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
    cout << f[m] << endl;
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络原理(四):传输层协议 TCP/UDP

目录 应用层 传输层 udp 协议 端口号 报文长度&#xff08;udp 长度&#xff09; 校验和 TCP 协议 确认应答 超时重传 链接管理 滑动窗口 流量控制 拥塞控制 延时应答 捎带应答 总结 我们第一章让我们对网络有了一个初步认识&#xff0c;第二章和第三章我们通…

bounding box线性回归

#bounding box regression原理 如图所示绿色框为飞机的Ground Truth(GT)&#xff0c;红色为提取的positive anchors&#xff0c;即便红色的框被分类器识别为飞机&#xff0c;但是由于红色的框定位不准&#xff0c;这张图相当于没有正确的检测出飞机。所以我们希望采用一种方法对…

MQTT协议 详解

文章目录 一、啥是MQTT&#xff1f;1. MQTT协议特点2. 发布和订阅3. QoS&#xff08;Quality of Service levels&#xff09;QoS 0 —— 最多1次QoS 1 —— 最少1次QoS 2 —— 只有1次 二、MQTT 数据包结构1. MQTT固定头2. MQTT可变头 / Variable header3. Payload消息体 三、M…

Redis集群常用命令及说明

一、集群的特点 1、集群架构特点 &#xff08;1&#xff09;所有的redis节点彼此互联&#xff08;PING-PONG机制&#xff09;&#xff0c;内部使用二进制协议优化传输速度和带宽&#xff1b; &#xff08;2&#xff09;节点的fail是通过集群中超过半数的节点检测失效时才生效…

2023年5月广州/东莞/深圳产品经理认证NPDP招生简章

产品经理国际资格认证NPDP是新产品开发方面的认证&#xff0c;集理论、方法与实践为一体的全方位的知识体系&#xff0c;为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会&#xff08;PDMA&#xff09;成立于1979年&#xff0c;是…

7.Shuffle详解

1.分区规则 ps."&"指的是按位与运算&#xff0c;可以强制转换为正数 ps."%",假设reduceTask的个数为3&#xff0c;则余数为0&#xff0c;1&#xff0c;2正好指代了三个分区 以上代码的含义就是对key的hash值强制取正之后&#xff0c;对reduce的个数取…

《可穿戴环形生物阻抗装置连续无袖血压监测》阅读笔记

目录 一、论文简介 二、十个问题 参考文献 一、论文简介 本文提出了一种基于环形生物阻抗传感器的连续无袖血压监测方法。该方法利用可穿戴环形生物阻抗装置实现连续无袖血压监测&#xff0c;并通过优化电极与皮肤接触点来提高信号灵敏度。实验结果表明&#xff0c;该方法可…

【动态规划】背包问题

目录 一:思路简介 二&#xff1a;0-1 背包 三&#xff1a;完全背包 四&#xff1a;多重背包 五&#xff1a;分组背包 一:思路简介 n 个物品&#xff0c;容量为V的背包 Vi 体积 Wi 价值(权重) 二&#xff1a;0-1 背包 每件物品最多只能用1次&#xff08;要么0次&…

给httprunnermanager接口自动化测试平台加点功能(一)

文章目录 一、背景1.1、部署过程略二、使用过程2.1、新增接口列2.2、实现搜索效果三、总结 一、背景 https://github.com/httprunner/HttpRunnerManager.git从github上找的接口测试平台&#xff0c;引入公司作为测试协同测试的平台&#xff0c;底层框架基于httprunner(requests…

【单目标优化算法】杂草优化算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

这些使用工具大推荐,现在知道不晚

1.Snip Snip是一款截图软件&#xff0c;它突出的优点就是可以制作滚动截图。 例如&#xff1a;对整个网页进行截图&#xff0c;使用Snip即可轻松获取&#xff0c;无需处理水印。 2.Sleep Cycle 快节奏、高压力的生活导致我们越来越晚睡觉&#xff0c;睡眠质量越来越差。 想提…

Python学习9:对指定r计算圆的面积(python123)

平台&#xff1a;python123 题目描述: 编写函数getCircleArea(r),对给定的参数r计算圆的面积&#xff0c;并返回首先读入n&#xff08;n>0&#xff09;&#xff0c;然后依次读入n个半径r1,r2,...,rn,以这些半径为参数依次调用getCircleArea函数&#xff0c;得到对应圆的面…

3.动态规划(0x3f:从周赛中学算法 2022下)

来自0x3f 【从周赛中学算法 - 2022 年周赛题目总结&#xff08;下篇&#xff09;】&#xff1a;https://leetcode.cn/circle/discuss/WR1MJP/ 【【灵茶山艾府】2022 年周赛题目总结&#xff08;上篇&#xff09;】https://leetcode.cn/circle/discuss/G0n5iY/ 学习动态规划是否…

( 栈和队列) 503. 下一个更大元素 II ——【Leetcode每日一题】

❓503. 下一个更大元素 II 难度&#xff1a;中等 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元素 是按数组遍历顺序&#xff0c;这个数字…

为何越来越多人不喜欢“试用期六个月”的公司?网友:感觉不靠谱

众所周知&#xff0c;任何一份工作都有试用期&#xff0c;一般是三月左右。但如果你遇到试用期达到半年的公司&#xff0c;你会不会进入&#xff1f; 近日&#xff0c;就有人遇到了此类公司&#xff0c;并对是否要进入该公司犹豫不决。他在论坛上发帖求助&#xff1a;大家是怎…

京城、京味、京韵:从一台服务器看数字北京

北京&#xff0c;既是首善之都&#xff0c;也是数字化创新之城。 早在1999年&#xff0c;北京就基于整座城市的信息化建设方案&#xff0c;率先提出了“数字北京”。后来&#xff0c;数字北京的魅力在奥运会期间大放异彩&#xff0c;受到了全球高度认可。如今&#xff0c;数字经…

【Python】【进阶篇】10、Django中间件

目录 Django中间件1. Django默认自带中间件1&#xff09;中间的执行与响应顺序2&#xff09;在调用视图之前3&#xff09;在调用视图之后 2. 中间件的作用总结 Django中间件 中间件是一个插件系统&#xff0c;嵌入在 Django 的 Request 和 Response 之间执行&#xff0c;可以对…

使用@Bean注解指定初始化和销毁的方法

bean的生命周期 通常意义上讲的bean的生命周期&#xff0c;指的是bean从创建到初始化&#xff0c;经过一系列的流程&#xff0c;最终销毁的过程。只不过&#xff0c;在Spring中&#xff0c;bean的生命周期是由Spring容器来管理的。在Spring中&#xff0c;我们可以自己来指定be…

apple pencil有买的必要吗?便宜的平替电容笔推荐

在当今世界&#xff0c;电容笔就已经成为一种热门的电子产品&#xff0c;其的各项性能也在不断改进。因此&#xff0c;如何挑选一款性价比高的电容笔成为大家关心的焦点&#xff0c;越来越多的人开始追求更好更廉价的电容笔。那么&#xff0c;哪个品牌的电容笔价格更实惠、性价…

工业设备巨头MSC Industrial Supply的供应链建设——EDI

MSC Industrial Supply提供广泛的工业用品和解决方案&#xff0c;包括切削工具、测量工具、金属加工和设备维护工具、劳动保护用品、工业设备等。MSC Industrial Supply的供应商来自全球各地&#xff0c;包括多个行业的领先品牌&#xff0c;例如Kennametal、Sandvik Coromant、…