首页 > 其他分享 >背包问题 持续更新中!

背包问题 持续更新中!

时间:2024-08-09 18:25:46浏览次数:14  
标签:背包 int max 持续 更新 体积 物品 1000

背包九讲

一、01背包问题

题目概览

有 N N N件物品和一个容量是 V V V的背包。每件物品只能使用一次。
第 i i i件物品的体积是 v i v_i vi​,价值是 w i w_i wi​。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数 N , V N,V N,V用空格隔开,分别表示物品数量和背包容积。
接下来有 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,V≤1000
0 < v i , w i ≤ 1000 0<v_i,w_i≤1000 0<vi​,wi​≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8

(一)先使用DP去做

思路:

f [ i ] [ j ] f[i][j] f[i][j]表示只看前i个物品,总体积是j的情况下,总价值最大是多少

r e s u l t = m a x ( f [ n ] [ 0 − v ] ) result = max(f[n][0-v]) result=max(f[n][0−v])

状态转移公式:

  1. 不选第i个物品, f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j] = f[i - 1][j] f[i][j]=f[i−1][j];
  2. 选第i个物品, f [ i ] [ j ] = f [ i − 1 ] [ j − v [ i ] ] f[i][j] = f[i - 1][j - v[i]] f[i][j]=f[i−1][j−v[i]]

f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − v [ i ] ] ) f[i][j] = max(f[i - 1][j],f[i - 1][j - v[i]]) f[i][j]=max(f[i−1][j],f[i−1][j−v[i]])

初始化工作: f [ 0 ] [ 0 ] = 0 f[0][0] = 0 f[0][0]=0


代码实现:
#include<bits/stdc++.h>
using namespace std;

const int N = 1010;

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

(二)进行优化

思路:

目标一:省掉一维空间

一共就二维,所以,我们不妨每一维都试一试。

  1. 省去j那一维

    显而易见,所有的操作都是基于这一维的,如果没有这一维,无法进行转移。
    换一种说法,省去j这一维, f [ i ] f[i] f[i]数组的含义变成了前i个物品,总价值是多少,好像不太行吧。。。

  2. 省去i那一维

    仔细观察,不难发现,所有的状态均是由i - 1那一维转移过来的,所以,可以尝试使用滚动数组
    即 f [ j ] f[j] f[j]中原先存的是 f [ i − 1 ] [ j ] f[i - 1][j] f[i−1][j]的数据,更新后变为 f [ i ] [ j ] f[i][j] f[i][j]的数据
    我们先尝试,强行去掉一维
    主体代码:

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

    那么,就有问题了,由于j是从小到大循环, f [ j − v [ i ] ] f[j - v[i]] f[j−v[i]],它好像已经被更新过了,它对应的是原先的 f [ i ] [ j − v [ i ] ] f[i][j - v[i]] f[i][j−v[i]],与我们想象的不符。

    所以,我们让 f [ j ] f[j] f[j]先更新,再让 f [ j − v [ i ] ] f[j - v[i]] f[j−v[i]]先更新即可。实现也非常简单,只需要把j倒着循环即可。

    目标二:少一个循环

    找答案的循环不需要啦!

    原因:
    初始化的时候,把所有的 f [ i ] f[i] f[i]都初始化成了0

    f [ m ] f[m] f[m]就是体积为 m m m的方案。


代码实现:
#include<bits/stdc++.h>
using namespace std;

const int N = 1010;

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

二、完全背包问题

题目概览:

有 N N N种物品和一个容量是 V V V的背包,每种物品都有无限件可用。
第 i i i种物品的体积是 v i v_i vi​,价值是 w i w_i wi​。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

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

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

输出格式

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

数据范围

0 < N , V ≤ 1000 0<N,V≤1000 0<N,V≤1000
0 < v i , w i ≤ 1000 0<v_i,w_i≤1000 0<vi​,wi​≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10

思路与解

对比 01 01 01背包,区别在于,每个物品有无限件可以选。

思路:

f [ i ] f[i] f[i]表示总体积是i的情况下,最大价值是多少
r e s u l t = m a x ( f [ 0 … m ] ) result = max(f[0…m]) result=max(f[0…m])
刚开始的想法肯定很朴实,会这样写:

for(int i = 0;i < n;i++)
    for(int j = m;j >= v[i];j--)//到这里都和01背包一样
        for(int k = 0;k * v[i] <= j;k++)//依次尝试能放多少
            f[j] = max(f[j],f[j - k * [i]] * w[i]);

但是时间复杂度太大了。。

优化:

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

因为是从小到大枚举,所以 f [ j − v [ i ] ] f[j - v[i]] f[j−v[i]]已经算过了
考虑前 i i i个物品,包括第 i i i个物品,可能里面已经有一些第 i i i个物品了

证明:
数学归纳法

  1. 假设考虑前 i − 1 i-1 i−1个物品之后,所有的 f [ j ] f[j] f[j]都是正确的

  2. 来证明:考虑完第 i i i个物品后,所有的 f [ j ] f[j] f[j]也都是正确的
    对于某个 j j j而言,如果最优解中包含 k k k个 v [ i ] v[i] v[i];
    从小到大枚举的时候,一定会枚举到 f [ j − f ∗ v [ i ] ] f[j - f * v[i]] f[j−f∗v[i]],那么这个状态就会用 f [ j − k ∗ v [ i ] − v [ i ] ] + w [ i ] f[j - k * v[i] - v[i]] + w[i] f[j−k∗v[i]−v[i]]+w[i]来更新它。
    枚举到 f [ j − ( k − 1 ) ∗ v [ i ] − v [ i ] ] + w [ i ] f[j - (k - 1) * v[i] - v[i]] + w[i] f[j−(k−1)∗v[i]−v[i]]+w[i]包含1个 v [ i ] v[i] v[i]

    所以 f [ j ] f[j] f[j]就一定枚举到有 k k k个 v [ i ] v[i] v[i]的情况


代码实现
#include<bits/stdc++.h>
using namespace std;

const int N = 1010;

int n,m;
int f[N];

int main(){
    cin >> n >> m;
    
    for(int i = 0;i < n;i++){
        int v,w;
        cin >> v >> w;
        for(int j = v;j <= m;j++)
            f[j] = max(f[j],f[j - v] + w);
    }
    
    cout << f[m];//与上一题的原因一样,不需要比较
    
    return 0;
}

三、多重背包问题

题目概览:

有 N N N种物品和一个容量是 V V V的背包。
第 i i i种物品最多有 s i s_i si​件,每件体积是 v i vi vi,价值是 w i wi wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数, N N N, V V V,用空格隔开,分别表示物品种数和背包容积.
接下来有 N N N行,每行三个整数 v i v_i vi​, w i w_i wi​, s i s_i si​,分别表示第 i� 种物品的体积、价值和数量。

输出格式

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

数据范围

0 < N , V ≤ 100 0<N,V≤100 0<N,V≤100
0 < v i , w i , s i ≤ 100 0<v_i,w_i,s_i≤100 0<vi​,wi​,si​≤100(注意,下面会改)

输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10

(一)最暴力的方法:

思路:

f [ i ] f[i] f[i]总体积是 i i i的情况下,最大价值是多少

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

将f的所有值初始化为 0 0 0,结果为 f [ m ] f[m] f[m]

代码与解
#include<bits/stdc++.h>
using namespace std;

const int N = 105;
int n,m;
int f[N];

int main(){
    cin >> n >> m;
    for(int i = 0;i < n;i++){
        int v,w,s;
        cin >> v >> w >> s;
        for(int j = m;j >= 0;j--)
            for(int k = 1;k <= s && k * v <= j;k++)
                f[j] = max(f[j],f[j - k * v] + k * w);
                
    }
    
    cout << f[m] << endl;
    return 0;
}

(二)二进制优化

题目不变

数据范围

0 < N ≤ 1000 0<N≤1000 0<N≤1000
0 < V ≤ 2000 0 < V \le 2000 0<V≤2000
0 < v i , w i , s i ≤ 2000 0<v_i,w_i,s_i\le2000 0<vi​,wi​,si​≤2000

思路:
  1. 把一个多重背包问题变成一个01背包问题
    我们可以把物品重复 s s s份,放到物品堆里去,每个物品就独立了,成功转化为一个01背包

例如,我们要把7拆成不同的方案
笨方法:1 1 1 1 1 1
每个1都有选和不选

聪明一下,我们只需要1,2,4,就可以表示出来1~7的所有数了

  1. 那么,回归到题目,只需要 l o g ( s ) log(s) log(s)上取整个数即可

    时间复杂度来到了可观的 1000 × 11 × 2000 = 2 × 1 0 7 1000\times11\times2000 = 2 \times 10 ^ 7 1000×11×2000=2×107

代码实现
#include<bits/stdc++.h>
using namespace std;

const int N = 2010;
int n,m;
int f[N];

struct Good{
    int v,w;
};

int main(){
    vector<Good> goods;
    cin >> n >> m;
    for(int i = 0;i < n;i++){
        int v,w,s;
        cin >> v >> w >> s;
        for(int k = 1;k <= s;k *= 2){
            s -= k;
            goods.push_back({v * k,w * k});
        }
        if(s > 0) goods.push_back({v * s,w * s});
    }
    
    for(auto good:goods)
        for(int j = m;j >= good.v;j--)
            f[j] = max(f[j],f[j - good.v] + good.w);
    
    cout << f[m] << endl;
    return 0;
}

多重背包还有单调队列优化,还有以下板块持续更新!

四、混合背包问题

五、二维费用的背包问题

六、分组背包问题

七、背包问题求方案数

八、背包问题求具体方案

九、有依赖的背包问题

标签:背包,int,max,持续,更新,体积,物品,1000
From: https://blog.csdn.net/Lucky_Threewsr/article/details/141068739

相关文章

  • Android 13 移植EthernetSettings/Ethernet更新
    移植EthernetSettingsAndroid13在Settings搜索没有发现以太网设置,应该是移除了,但是客户的设备需要,所以移植Android11的.以太网相关的功能在Android13中进行模块化,提取到packages/modules/Connectivity/中,EthernetManager相关代码从framework移到packages/modules/Conne......
  • 隐马尔可夫模型与隐半马尔可夫模型:状态持续时间的影响与应用场景对比
    目录前言隐马尔可夫模型(HMM)与隐半马尔可夫模型(HSMM)的区别隐马尔可夫模型(HMM)隐半马尔可夫模型(HSMM)HSMM的特点应用场景实现复杂度隐马尔可夫模型(HMM)与隐半马尔可夫模型(HSMM)对比表结论前言   在序列数据分析领域,隐马尔可夫模型(Hidd......
  • Stable Diffusion WebUI v1.10.0重大更新,支持SD3!
    Hello,大家好!前不久,SDWebUI的作者AUTOMATIC1111终于把它更新到了v1.10.0,这次不仅修复以往的一些BUG,提升了一些性能,这次还支持了SD3_medium.safetensors模型以及SD3_LoRA模型,同时还支持T5系列的encoder模型,让我们一起来看看这次更新了哪些内容。更新内容总共有87项更新:1.......
  • Flux 生态更新总结
    :FLUXTinyVAE训练脚本FluxAIGridComparisons::FLUX生成的发型、服装、国籍、年龄等各种图像对比集合ComfyUI:适配 xlabsFluxcontrolnetcomfyui-replicateInstantX/FLUX.1-dev-Controlnet-Canny-alpha:又一个CannyControlNet模型daniel5984/flux_TrainingFLUX.1-DEV-Ca......
  • 如何从我的 Python 应用程序更新我的 Facebook Business 令牌?
    我有一个使用FacebookBusiness库的Python应用程序。因此,我需要使用Facebook提供的令牌来访问我的见解并操纵它们。但是,这个令牌有一个很长的到期日期,但我想知道是否有办法自动更新这个令牌在我的应用程序中,这样它就不会停止运行。当然可以!你可以使用Facebook提......
  • Tkinter 按钮不更新变量
    我有一个类中有两个单选按钮的GUI,使用tkinter的第一个单选按钮旨在将变量save_to_excel设置为True,而第二个单选按钮应为false,这是为了让用户确定是否要保存信息作为Excel工作表或文本文档。我有一个旧版本的代码,可以工作并正确更新变量,但是新版本必须更改某些内容,而新版......
  • 构建即时通讯应用:Spring boot高效集成WebSocket、Stomp协议完成前后端持续通信
    1.引入依赖在你的SpringBoot项目的pom.xml中添加以下依赖:<dependencies><!--SpringBootStarterThymeleaf--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-st......
  • DzzOffice系统与插件更新日志
    2024/07/23PDFTron1.支持将编辑后的PDF文件保存到Dzz中:实现了编辑与存储的无缝对接,简化了文件处理流程,减少了用户的操作步骤,提高了工作效率。2.对接Dzz的文件权限管理:用户在保存PDF文件时,可以自动应用DzzOffice中设置的文件权限规则。这意味着文件的查看、编辑、下载等权......
  • 「Android面试」Android 子线程为什么直接更新UI?
    本文将从子线程不能更新UI的直接原因、根本原因、Android如何做到限制以及子线程该如何正确更新UI四个方向回答问题。【直接原因】在子线程中更新UI会怎样?程序会出现以下错误:Onlytheoriginalthreadthatcreatedaviewhierarchycantouchitsviews. 【根本原因......
  • CSP模拟 小 trick 总结 (持续施工中)
    虽然这篇博客来的有点晚,但还是写了,欢迎dalao补充(1、分块、莫队有关:(1):一个真正的回滚莫队(感谢Qyun的讲解)$\\\\\\\\$学习回滚莫队的时候,我们经常会在回滚时使用memcpy来恢复以前的版本,但众所周知--memset和memcpy常数巨大,破坏了莫队$O(n\sqrtn)$的时间复杂度,导......