背包问题

本文详细解析了三种背包问题:01背包、完全背包和多重背包。针对每种问题提供了清晰的题解思路及优化后的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.01背包

问题描述

n个物品,每个物品的重量为wi和pi,且每种物品只有一个,现有一个背包,容量为W,往背包里面放物品,要是放的物品价值最大。

题解

对于每种物品来说,只有放与不放两种可能。dp[i][j] 表示当容量是j的时候,第i个物品放或者不放的情况下背包的最大价值。同时我们会发现dp[i]的数组只和dp[i-1]有关,所以,我们可以对普通的01背包进行优化,将dp优化成一维数组。

代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100;
int n , W;
int w , p;
int dp[N];
int main(){
    cin >> n >> W;
    memset(dp , 0 , sizeof(dp));
    for(int i = 1; i <= n;i++){
        cin >> w >> p;
        for(int j = W;j >= w;j--){
            dp[j] = max(dp[j] , dp[j - w] + p);//关键的动态规划表达式
        }
    }
    cout << dp[W] << endl;
    return 0;
}

2.完全背包

问题描述

n个物品,每个物品的重量为wi和pi,每种物品有若干个,现有一个背包,容量为W,往背包里面放物品,要是放的物品价值最大。

题解

如果说01背包是物品放和不放的问题,那个完全背包就是物品放不放和放几个的问题。所以最简单的思路就是再套一层循环,判断放0个、1个……哪种最好。

代码
#include<iostream>
using namespace std;
const int N = 100;
int n , W;
int w, p; 
int dp[N][10000];

int main(){
    cin >> W >> n;
    for(int i = 1;i <= n;i++){
        cin >> w >> p;
        for(int j = 1;j <= W;j++){
            int num = j / w;
            for(int t = 0;t <= num;t++){
                dp[i][j] = max(dp[i - 1][j] , dp[i][j - t * w] + t * p);
            }
        }
    }
    cout << dp[n][W] << endl; 
    return 0;
}
优化

首先和01背包一样,可以对dp数组降维,将dp降成一维数组。其次,我们可以将第三重循环去掉,因为就是往背包里面放0个,1个,…..,一直到W/w[i]个,取其最优解。

最优代码
#include<iostream>
using namespace std;
const int N = 100;
int n , W;
int w, p; 
int dp[10000];

int main(){
    cin >> W >> n;
    for(int i = 1;i <= n;i++){
        cin >> w >> p;
        for(int j = w;j <= W;j++){
            dp[j] = max(dp[j] , dp[j - w] + p); //关键的动态规划表达式,和01背包的代码进行对比
        }
    }
    cout << dp[W] << endl; 
    return 0;
}

3.多重背包

问题描述

n个物品,每个物品的重量为wi和pi,每种物品有ti个,现有一个背包,容量为W,往背包里面放物品,要是放的物品价值最大。

题解

如果说01背包是物品放和不放的问题,那个完全背包就是物品放不放和放几个的问题。那么当多重背包中的ti ≤ W / wi 时,可以看作一个01背包;当多重背包中的ti > W / wi 时,可以看作一个完全背包。

代码:
void ZeroOnePack(int w,int p){
    for(int j = v;j >= w;j--){
        if(dp[j - w]+p > dp[j])
            dp[j] = dp[j - w]+p;
    }
}

void CompletePack(int w,int p){
    for(int j = w;j <= v;j++){
        if(dp[j - w]+p > dp[j])
            dp[j] = dp[j - w]+p;
    }
}

void MultiPack(int w,int p,int num){
    if(num > v/w){
        //CompletePack
        CompletePack(w , p);
    }else{
        //ZeroOnePack
        int k = 1;
        while(k < num){
            ZeroOnePack(k*w,k*p);
            num -= k;
            k <<= 1;
        }
        ZeroOnePack(num * w , num * p );    
    }
}
例题以及AC代码

悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
根据t的数量拆分成0-1背包和完全背包两种背包问题求解。

#include<iostream>
#include<cstring>
using namespace std;
int w , p , t ,num;
int dp[400000 + 10];
int n,v;

void ZeroOnePack(int w,int p){
    for(int j = v;j >= w;j--){
        if(dp[j - w]+p > dp[j])
            dp[j] = dp[j - w]+p;
    }
}

void CompletePack(int w,int p){
    for(int j = w;j <= v;j++){
        if(dp[j - w]+p > dp[j])
            dp[j] = dp[j - w]+p;
    }
}

void MultiPack(int w,int p,int num){
    if(num > v/w){
        //CompletePack
        CompletePack(w , p);
    }else{
        //ZeroOnePack
        int k = 1;
        while(k < num){
            ZeroOnePack(k*w,k*p);
            num -= k;
            k <<= 1;
        }
        ZeroOnePack(num * w , num * p );    
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); 
    int exm;
    cin >> exm;
    while(exm--){
        memset(dp , 0 , sizeof(dp));
        cin >> v >> n;
        for(int i = 1;i <= n;i++){
            cin >> w >> p >> t;
            MultiPack(w , p , t);
        }
        cout << dp[v] << endl;
      }
    return 0;  
}   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值