背包DP

给定一个重量限制,和几种不同物品的价值和重量,每种物品只有一个,寻找能拿到的最大价值是多少

01背包问题根本指的是,同种物品有且最高只有一个,在给定限制下,如何获得最大收益

枚举:找出所有组合
关键的状态转移方程如下:
在这里插入图片描述

#include<iostream>  //正常情况下cin和cout的I/O效率远不如stdio.h中的函数
using namespace std;
const int maxn = 1000;
int dp[20][20];
int value[20],weight[20];
 
int main(){
    int n,limitw;
    cin>>n>>limitw; //物品个数,背包大小
    for (int i=1;i<=n;i++){
        cin>>value[i]>>weight[i];
    }
    cout<<"0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20\n"; //更好的观察算法
    for(int i=1;i<=n;i++){
        for(int j=0;j<=limitw;j++){
            /*算法的唯一要点*/
            if(j>=weight[i]){
                dp[i][j]=max(dp[i-1][j-weight[i]]+value[i],dp[i-1][j]);
            }else{
                dp[i][j]=dp[i-1][j];    //如果放不下,继承上一轮的结果
            }
            cout<<dp[i][j]<<" ";    //更好的观察算法
        }
        cout<<"\n"; //更好的观察算法
    }
    cout<<dp[n][limitw];
    return 0;
}

一维优化01背包:

#include<iostream>
using namespace std;
const int maxn = 1000;
int dp[20];
int value[20],weight[20];
 
int main(){
    int n,limitw;
    cin>>n>>limitw;
    for (int i=1;i<=n;i++){
        cin>>value[i]>>weight[i];
    }
    cout<<"0 1 2 3 4 5 6 7 8 9 10\n";   //更好的观察算法
    for(int i=1;i<=n;i++){
        for(int j=limitw;j>=weight[i];j--){
            dp[j]=max(dp[j-weight[i]]+value[i],dp[j]);
            cout<<dp[j]<<" ";   //更好的观察算法
        }
        cout<<"\n"; //更好的观察算法
    }
 
    cout<<dp[limitw];
    return 0;
}

多重背包:
有N种物品,第i种物品的体积是Ci,价值是Wi,每种物品的数量都是有限的,为Ni。现有容量为V的背包,放入若干物品,在总体积不超过V的条件下,使总价值尽可能大。

在不需要记录物品种类的情况下,01背包就是每种物品数量为1的多重背包问题,所以可以依旧可以采用01背包的算法,把物品拆分成一件一件的。

#include<iostream>
using namespace std;
int dp[21][1010];       //物品上限,背包大小
int w[10],c[21],n[21];  //价值,体积,数量
int main(){
    int N,V;
    cin>>N>>V;
    for(int i=1;i<=N;i++){
        cin>>w[i]>>c[i]>>n[i];
    }
    for(int i=1;i<=N;i++){  //第几件物品
        for(int j=0;j<=V;j++){  //背包大小
            for(int k=0;k<=n[i];k++){   //这里其实把物品当做一个一个往里面放
                if(j>=c[i]*k){
                    dp[i][j]=max(dp[i-1][j-c[i]*k]+w[i]*k,dp[i][j]);
                }
            }
        }
    }
    cout<<dp[N][V]<<endl;
    return 0;
}

一维DP优化:

#include<iostream>
using namespace std;
int dp[1010];       //背包大小
int w[10],c[21],n[21];  //价值,体积,数量
int main(){
    int N,V;
    cin>>N>>V;
    for(int i=1;i<=N;i++){
        cin>>w[i]>>c[i]>>n[i];
    }
    for(int i=1;i<=N;i++){  //第几件物品
        for(int j=V;j>=0;j--){  //背包大小,优化空间复杂度一定要反着放
            for(int k=0;k<=n[i];k++){   //这里其实把物品当做一个一个往里面放
                if(j>=c[i]*k){
                    dp[j]=max(dp[j-c[i]*k]+w[i]*k,dp[j]);
                }
            }
        }
    }
    cout<<dp[V]<<endl;
    return 0;
}

完全背包:
在多重背包问题上把每种物品的数量取消限制
解决思路:转换成多重背包问题

其实可以直接使用多重背包来解决,只不过时间复杂度接近o(n v v),因为虽然物品数量没有上限,但其实物品数量的上限就是背包的上限,只需要处理数量*体积<剩余空间。

观察后发现,之前的多重背包问题中,第三重循环仅仅是为了限制数量

和01背包的不同之处在于,01背包每一轮记录的都是同一件物品的结果,而完全背包记录的是一种物品存放多次,具体体现在dp的更新上,01背包我们在放下当前物品时之后从上一轮拿出之前的记录在存放之前的物品,然而完全背包是每一轮把当前品种放慢,之后每次都取当前能得到的最大值,我个人觉得是有一种贪心的思想在里面。

#include<iostream>
using namespace std;
int dp[21][1010];       //物品上限,背包大小
int w[10],c[21];  //价值,体积
int main(){
    int N,V;
    cin>>N>>V;
    for(int i=1;i<=N;i++){
        cin>>w[i]>>c[i];
    }
    for(int i=1;i<=N;i++){  //第几件物品
        for(int j=0;j<=V;j++){  //背包大小
            if(j>=c[i]){
                dp[i][j]=max(dp[i][j-c[i]]+w[i],dp[i-1][j]);
            }else{
                dp[i][j]=dp[i-1][j];
            }
 
        }
    }
    cout<<dp[N][V]<<endl;
    return 0;
}

一维DP优化:

#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
int dp[1010];       //背包大小
int w[10],c[21];  //价值,体积
int main(){
    int N,V;
    cin>>N>>V;
    for(int i=1;i<=N;i++){
        cin>>w[i]>>c[i];
    }
    for(int i=1;i<=N;i++){  //第几件物品
        for(int j=1;j<=V;j++){  //背包大小
            dp[j]=max(dp[j-c[i]]+w[i],dp[j]);
        }
    }
    cout<<dp[V]<<endl;
    return 0;
}

分组背包问题:
问题
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

算法
这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}
使用一维数组的伪代码如下:

for 所有的组k
for v=V…0
for 所有的i属于组k
f[v]=max{f[v],f[v-c[i]]+w[i]}

“for v=V…0”这一层循环必须在“for 所有的i属于组k”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。

int w[], z[], b[], g[][], n, t;     //w是物品重量,z是物品价值,
//b是某x组里面有几个物品,g是某i组里面的第k个物品对应的原顺序号码
    for (i = 1; i <= n; i++) {
        cin >> w[i] >> z[i] >> x;
        t = max(t, x);  //求小组组数
        b[x]++;
        g[x][b[x]] = i;
    }
    for (i = 1; i <= t; i++) {             //小组
        for (j = v; j >= 0; j--) {         //容量
            for (k = 1; k <= b[i]; k++) {  //小组中的物品
                if (j >= w[g[i][k]]) {     //小组i中物品k的容量
                    dp[j] = max(dp[j], dp[j - w[g[i][k]]] + z[g[i][k]]);  //状态转移方程
                }
            }
        }
    }

ps:转载于各网页,仅做学习用途,侵删

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值