DP(2)--背包DP(0-1 背包,完全背包,多重背包)

news2025/3/1 0:24:57

滚动数组: 让数组滚动起来,每次都使用固定的几个存储空间,来达到压缩,节省存储空间的作用。
一般用于递推和动态规划中

一维数组
比如:求斐波那契数列第100项
long long arr[3];
arr[0] = 1;
arr[0] = 1;
for (int i = 2; i < 100; ++i)
    arr[i%3] = arr[(i-1)%3] + arr[(i-2)%3];

二维数组
int dp[1000][1000];
for (int i = 1; i < 1000; ++i)
    for (int j = 0; j < 1000; ++j)
        dp[i][j] = dp[i-1][j] + dp[i][j-1];

运用滚动数组
int dp[2][1000];
for (int i = 1; i < 1000; ++i)
    for (int j = 0; j < 1000; ++j)
        dp[i%2][j] = dp[(i-1)%2][j] + dp[i%2][j-1];
        
0-1 背包
有n个物品和一个容量为c的背包,每个物品有重量w[i]和v[i]两种属性,
选取若干个物品放入背包使背包中的物品总价值最大且背包中物品的总重量不超过背包的容量。
设DP状态f[i][j]为在前i个物品中选取,容量为j的背包所能达到的最大价值
状态转移: 假设当前已经处理好了前i-1个物品的所有状态,
那么对于第i个物品,当其不放入背包时,背包的剩余容量不变,背包中物品的总价值也不变,故这种情况的最大价值为f[i-1][j];
当其放入背包时,背包的剩余容量会减小w[i],背包中物品的总价值会增大 v[i],故这种情况的最大价值为 f[i-1][j-w[i]]+v[i].
由此可以得出状态转移方程:
    f[i][j] = max(f[i-1][j], f[i-1][j-w[i]]+v[i])
由于对f[i]有影响的只有f[i-1],可以去掉第一维,直接用f[i]表示处理到当前物品时背包容量为i的最大价值
 
洛谷 2871
#include <iostream>
using namespace std;

//int dp[2][12881];
int dp[12881];
int w[3403];
int v[3403];

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

    // 常规滚动数组实现
    //for (i = 1; i <= n; ++i)
    //    for (j = 1; j <= c; ++j)
    //        if (j >= w[i])
    //            dp[i%2][j] = max(dp[(i-1)%2][j], dp[(i-1)%2][j-w[i]]+v[i]);
    //        else
    //            dp[i%2][j] = dp[(i-1)%2][j];

    //cout << dp[n%2][c] << endl;

    // 对dp[i][]产生影响的只有dp[i-1][], 所以数组可以去掉一维
    for (i = 1; i <= n; ++i)
        for (j = c; j >= w[i]; --j)
            dp[j] = max(dp[j], dp[j-w[i]]+v[i]);

    cout << dp[c] << endl;

    return 0;
}


完全背包

完全背包与 0-1 背包的区别仅在于一个物品可以选取无限次,而非仅能选取一次。

由于, f[i][j-w[i]]已由f[i][j-2*w[i]]更新过,对于第i件物品,重复选取,实现如下:
   for (j = w[i]; j <= t; ++j) // j 按从小到大顺序枚举,可实现重复选取
    dp[j] = max(dp[j], dp[j-w[i]] + v[i]);

洛谷 1616
#include <iostream>
using namespace std;

long long dp[10000001];
int w[10001];
int v[10001];

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

    for (i = 1; i <= n; ++i)
        for (j = w[i]; j <= t; ++j) // j 按从小到大顺序枚举,可实现重复选取
            dp[j] = max(dp[j], dp[j-w[i]] + v[i]);

    cout << dp[t] << endl;

    return 0;
}


多重背包问题
多重背包与 0-1 背包的区别仅在于一个物品可以选取m次。

单调队列求滑动窗口最值问题
/*https://www.luogu.com.cn/problem/P1886 */
#include <iostream>
#include <list>
#include <vector>
using namespace std;

int arr[100001];

int main()
{
    int i, n, k;
    cin >> n >> k;
    for (i = 0; i < n; ++i)
        cin >> arr[i];

    list<int> ls1; 
    list<int> ls2;
    vector<int> vec1;
    vector<int> vec2;

    for (i = 0; i < n; ++i)
    {
        // 单调递减队列求滑动窗口最大值
        while (ls1.size() > 0 && arr[ls1.back()] < arr[i])  //  新加入元素不满足单调递减
            ls1.pop_back();
        ls1.push_back(i);
        if (i-ls1.front()+1 > k)   // 队列中元素数量超过窗口大小
        {
            ls1.pop_front();
        }
        if (i+1 >= k)
            vec1.push_back(arr[ls1.front()]);

        // 单调递增队列求滑动窗口最小值
        while (ls2.size() > 0 && arr[ls2.back()] > arr[i])
            ls2.pop_back();
        ls2.push_back(i);
        if (i-ls2.front()+1 > k)
        {
            ls2.pop_front();
        }
        if (i+1 >= k)
            vec2.push_back(arr[ls2.front()]);
    }

    for (i = 0; i < vec2.size(); ++i)
        cout << vec2[i] << " ";
    cout << endl;
    for (i = 0; i < vec1.size(); ++i)
        cout << vec1[i] << " ";
    cout << endl;


    return 0;
}

/* https://www.luogu.com.cn/problem/P1776 */

/*  朴素思想

for (i = 0; i < n; ++i)          // 枚举物品种类
{
    cin >> v >> w >> m;
    for (j = W; j >= w; --j)     // 枚举背包容量
        for (k = 1; k <= m; ++k) // 枚举该种物品的数量
            dp[j] = max(dp[j], dp[j - k*w] + k*v);
}


为了获取dp[j], 需要使用dp[j-w], dp[j-2w], ... , dp[j-k*w]的状态。
当取k件该种物品,需要为这k件物品预留k*w的空间,剩下的j-k*w的空间就是之前存储的状态。由于第二层循环的j是递减的,当下一次容量为j-1时,整个遍历过程可理解为向左平移一格。
依此类推,当j递减到j-w时就和初始的状态重合了,
所以根据第i个物品的w,我们可以将整个dp数组划分成w个等价类,
分别以0,1,2,…,w-1为开始元素。
用单调递减队列对
     每个等价类f[i][j]=max(f[i−1][j],f[i−1][j−w]+v,f[i−1][j−2w]+2v,....+f[i−1][j−m*w]+m*v)求最大值,
时间复杂度就可以降至O(n*m)。
可看做求大小为m的滑动窗口最大值问题
*/

#include <iostream>
#include <vector>
#include <list>
using namespace std;

int main()
{
    int n, W , i, j, k;
    int v, w, m;   // value, weight, amount
    cin >> n >> W;
    vector<int> dp(W+1, 0);
    vector<int> prev;      // 滚动数组,记录上一组dp[]的值
    list<int> ls;          // 单调递减队列求滑动窗口最大值,滑动窗口的大小为m[i]

    for (i = 0; i < n; ++i)
    {
        prev = dp;
        cin >> v >> w >> m;
        for (j = 0; j < w; ++j)
        {
            ls.clear();
            for (k = j; k <= W; k += w)
            {
                /* 对于当前种类的物品,他的数量最多有m个,重量为w,
                 要用到的最早的状态是全取时k-s*w,所以要保证队首元素要在这个范围内。*/
                if (!ls.empty() && ls.front() < k - w*m)
                    ls.pop_front();

                /* k - ls[head]  背包剩余容量
                   (k - ls[head])/w 背包剩余容量能存几个重量为w的物品
                   (k - ls[head])/w*v 取这几个物品的价值 */
                if (!ls.empty())
                    dp[k] = max(dp[k], prev[ls.front()] + (k-ls.front())/w*v);

                /* 为了保持容量队列单调递减,背包容量k的dp值应小于队尾元素的dp值 */
                while (!ls.empty() && prev[ls.back()] + (k-ls.back())/w*v <= prev[k])
                    ls.pop_back();

                ls.push_back(k);
            }
        }
    }

    cout << dp[W] << endl;

    return 0;
}

/*
  任何一个非负整数,可以用一个以1为首项,2为公比的等比数列中的数来表示
  通过二进制优化,可将多重背包转换为0-1背包
*/

#include <iostream>
using namespace std;
const int MAXN = 10000;
int arrV[MAXN]; // value
int arrW[MAXN]; // weight
int dp[100001];

int main()
{
    int n, W , i, j, k, cnt = 0;
    int v, w, m;   // value, weight, amount
    cin >> n >> W;

    for (i = 0; i < n; ++i)
    {
        cin >> v >> w >> m;
        k = 1;
        do {
            m -= k;
            arrV[cnt] = v*k;
            arrW[cnt] = w*k;
            ++cnt;
            k = k << 1;
        } while (k <= m);

        if (m > 0)
        {
            arrV[cnt] = v*m;
            arrW[cnt] = w*m;
            ++cnt;
        }
    }

    for (i = 0; i < cnt; ++i)
        for (j = W; j >= arrW[i]; --j)
            dp[j] = max(dp[j], dp[j - arrW[i]] + arrV[i]);

    cout << dp[W] << endl;

    return 0;
}
 

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

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

相关文章

Java 集合基础

文章目录一、集合概念二、ArrayList1. 构造方法和添加方法2. 常用方法三、案例演示1. 存储字符串并遍历2. 存储学生对象并遍历3. 键盘录入学生对象并遍历一、集合概念 编程的时候如果要存储多个数据&#xff0c;使用长度固定的数组存储格式&#xff0c;不一定满足我们的需要&a…

Axios二次封装和Api的解耦

目录 一、axios三种基本写法 二、axios的二次封装 三、Api的解耦 一、axios三种基本写法 1&#xff09;get方法&#xff08;是最简单的&#xff09;&#xff1a; 写法二&#xff1a; 2&#xff09;post&#xff1a; 3&#xff09;axios请求配置 默认是get请求&#xff0c;如…

数据分析-深度学习 NLP Day2关键词提取案例

训练一个关键词提取算法需要以下几个步骤&#xff1a;1&#xff09;加载已有的文档数据集&#xff1b;2&#xff09;加载停用词表&#xff1b;3&#xff09;对数据集中的文档进行分词&#xff1b;4&#xff09;根据停用词表&#xff0c;过滤干扰词&#xff1b;5&#xff09;根据…

C语言学习_DAY_5_循环结构while和for语句【C语言学习笔记】

高质量博主&#xff0c;点个关注不迷路&#x1f338;&#x1f338;&#x1f338;&#xff01; 目录 I. 案例引入 II. while语句 III. do while语句 IV. for语句 前言: 书接上回&#xff0c;判断结构已经解决&#xff0c;接下来是另一种很重要的结构&#xff1a;循环结构的实…

深入Spring底层透析后置处理器之豁然开朗篇

目录前言Spring的后置处理器Bean工厂后置处理器Bean后置处理器自定义Component实现注解开发前言 看这篇文章之前&#xff0c;需要了解Bean创建的过程&#xff0c;本篇文章是接着bean创建的基本流程的续写 Bean创建的基本过程&#xff1a;http://t.csdn.cn/1lK2d Spring的后置处…

Python3 命名空间和作用域实例及演示

命名空间 先看看官方文档的一段话&#xff1a; 命名空间(Namespace)是从名称到对象的映射&#xff0c;大部分的命名空间都是通过 Python 字典来实现的。 A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries…

凌恩生物经典文章:孟德尔诞辰200周年,Nature Genetics礼献豌豆高质量精细图谱

本期为大家分享的文章是2022年发表在《Nature Genetics》上的一篇文章“Improved pea reference genome and pan-genome highlight genomic features and evolutionary characteristics”&#xff0c;作者通过结合三代pacbio测序、染色体构象捕获&#xff08;Hi-C&#xff09;测…

Meta分析在生态环境领域里的应用

Meta分析&#xff08;Meta Analysis&#xff09;是当今比较流行的综合具有同一主题的多个独立研究的统计学方法&#xff0c;是较高一级逻辑形式上的定量文献综述。20世纪90年代后&#xff0c;Meta分析被引入生态环境领域的研究&#xff0c;并得到高度的重视和长足的发展&#x…

企业什么要建设自有即时通讯软件系统

随着科技的不断发展&#xff0c;各种即时通讯软件也不断发展进步&#xff0c;而这也与企业的发展息息相关&#xff0c;因为每个人&#xff0c;每个企业都有属于自己的机密&#xff0c;属于自己的隐私。 钉钉&#xff0c;企业微信&#xff0c;等公有的即时通讯软件给企业带来便利…

微信社区小程序/h5/圈子论坛贴吧交友/博客/社交/陌生人社交/宠物/话题/私域/同城交友

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 小程序/app/H5多端圈子社区论坛系统,交友/博客/社交/陌生人社交,即时聊天,私域话题,社区论坛圈子,信息引流小程序源码,广场/微校园/微小区/微同城/ 圈子论坛社区系统&#xff0c;含完整…

扬帆优配|3300点半日游!上证指数冲高回落;再迎重磅利好!

今天早盘&#xff0c;A股冲高回落&#xff0c;上证指数3300点得而复失&#xff0c;深证成指也于12000点无功而返。 盘面上&#xff0c;煤炭、钢铁、房地产、才智政务等板块涨幅居前&#xff0c;酿酒、酒店餐饮、日用化工、IT设备等板块跌幅居前。北上资金净流入7.77亿元。 房地…

UML中常见的9种图

UML是Unified Model Language的缩写&#xff0c;中文是统一建模语言&#xff0c;是由一整套图表组成的标准化建模语言。UML用于帮助系统开发人员阐明&#xff0c;展示&#xff0c;构建和记录软件系统的产出。通过使用UML使得在软件开发之前&#xff0c; 对整个软件设计有更好的…

【数据库】Redis数据类型

目录 一&#xff0c; Key操作 1&#xff0c; 相关命令 2&#xff0c; 示例演示 二&#xff0c;字符串 String 1&#xff0c; 结构图 2&#xff0c;相关命令 3&#xff0c;示例演示 三&#xff0c; 列表 List 1&#xff0c; 结构图 2&#xff0c; 相关命令 3&#xf…

VO、DTO、BO、PO、DO区别

VO、DTO、BO、PO、DO区别 VO&#xff1a;&#xff08;View Object&#xff09;视图对象&#xff0c;一般位于Controller层&#xff0c;用于展示视图。DTO&#xff1a;&#xff08;Data Transfer Object&#xff09;数据传输对象&#xff0c; 即RPC 接口请求或传输出去的对象&a…

PMP值得考吗?含金量如何?

关于哪些证书可以考&#xff0c;这里我也不说其他的证书因为术业有专攻&#xff0c;其他证书的含金量估计我也没有更加专业的人士懂&#xff0c;我就推荐一下关于项目管理pmp证书的一些含金量吧&#xff01;对于想备考PMP的朋友或许有一些帮助。 一&#xff0c;PMP证书的价值体…

简易高并发内存池

文章目录从零实现一个高并发的内存池定长内存池定长内存池设计申请内存池用户申请用户归还&#xff08;释放&#xff09;总体代码测试对比&#xff08;malloc&#xff09;高并发内存池框架简介thread cache的设计申请部分释放部分第一层的测试central cache的设计申请部分释放部…

【Kubernetes】【十九】安全认证

第九章 安全认证 本章节主要介绍Kubernetes的安全认证机制。 访问控制概述 ​ Kubernetes作为一个分布式集群的管理工具&#xff0c;保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对Kubernetes的各种客户端进行认证和鉴权操作。 客户端 在Kubernetes集群…

【Kubernetes】【十七】数据存储 基本存储 EmptyDir HostPath NFS

第八章 数据存储 ​ 在前面已经提到&#xff0c;容器的生命周期可能很短&#xff0c;会被频繁地创建和销毁。那么容器在销毁时&#xff0c;保存在容器中的数据也会被清除。这种结果对用户来说&#xff0c;在某些情况下是不乐意看到的。为了持久化保存容器的数据&#xff0c;ku…

2023只会“点点点”,被裁只是时间问题,高薪的自动化测试需要掌握那些技能?

互联网已然是存量市场了&#xff0c;对人员规模的需求正在放缓。在存量市场里&#xff0c;冗余人员和低效人员会被淘汰、被外包。而优秀的人才也会一直受到招聘方的青睐。所以我们就看到了近期行业里冰火两重天的一幕&#xff0c;一边是大量的低端测试工程师被淘汰、求职屡屡碰…

ssh设置:免密登入、修改默认端口、禁止root登入、限制错误登入次数

服务器&#xff1a; 客户端&#xff1a; 在下面不再说明服务器和客户端。 1.修改ssh默认端口 是在服务器中设置。 该设置涉及三部分&#xff1a;sshd配置文件修改/增加新端口、Selinux添加新端口、Firewall开放新端口。 vim /etc/ssh/sshd.config&#xff0c;找到#Port行&…