背包问题大融合!

目录:

1.01背包(朴素版,朴素版2,一维滚动数组优化)

2.完全背包问题(朴素版,优化1,一维优化)

3.多重背包问题(朴素版,二分优化)

一.01背包问题

问题简述:有m个物品每个物品有对应价值ve[i]和对应体积t[i],求在有限背包容量te里能装的最大价值为多少

应该是最基础的背包问题了,核心代码dp[i][j]=dp[i-1][j-v[i]]+w[i];

思路:枚举物品并且枚举背包空间,讨论放与不放;

(1)朴素版1

//#iclude<bits/stdc++.h>
//using namespace std;
//
//const int N=1e2+5;
//const int N1=1e3+5;
//int t[N];//体积
//int ve[N];//价值
//int dp[N1][N1];
//int te,m;//背包总体积和物品数量
//
//int main(){
//    cin>>te>>m;
//    for(int i=1;i<=m;i++){
//        cin>>t[i]>>ve[i];//输入每个物品的体积和价值
//    }
//    
//    for(int i=1;i<=m;i++){//枚举物品个数(物品编号)
//        for(int j=1;j<=te;j++){//同时枚举空间
//            
//            if(j>=t[i]){//若放的下
//                dp[i][j]=max(dp[i-1][j],dp[i-1][j-t[i]]+ve[i]);

//则把前i个物品放到容量为j 的背包里的结果为,不拿(则把前i-1个物品放到容量为j的背包里 )和//拿(则把前i-1个物品放到容量为j-v[i]的背包里 )+当前物品价值的最大值
//            }
//            else{
//                dp[i][j]=dp[i-1][j];//否则就直接将i-1转移下来


//            }
//        }
//    }
//
//    cout<<dp[m][te];

//输出m个物品放到容量为te的背包里的最大价值,因为循环i,j从1开始,所以不用减一
//    return 0;
//} 

(2)朴素版2

//#include<bits/stdc++.h>
//using namespace std;
//
//const int N=1e2+5;
//const int N1=1e3+5;
//int t[N];
//int ve[N];
//int dp[N1][N1];
//int te,m;
//
//int main(){
//    cin>>te>>m;
//    for(int i=1;i<=m;i++){
//        cin>>t[i]>>ve[i];
//    }
//    
//    for(int i=1;i<=m;i++){
//        for(int j=1;j<=te;j++){
//            dp[i][j]=dp[i-1][j];//若放前边不写else
//            if(j>=t[i]){
//                dp[i][j]=max(dp[i][j],dp[i-1][j-t[i]]+ve[i]);//则前边是i,不是i-1
//            }
//            
//        }
//    }
//
//    cout<<dp[m][te];
//    return 0;
//} 

(3)一维滚动数组优化

#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
int dp[N];
int num,ve;
int n[N],vel[N];
int main(){
    cin>>num>>ve;
    for(int i=1;i<=num;i++){
        cin>>n[i]>>vel[i];
    }
    
    for(int i=1;i<=num;i++){
        for(int j=ve;j>=n[i];j--){ //倒过来可以不影响前边的,若正过来则要加一个temp数组
            dp[j]=max(dp[j],dp[j-n[i]]+vel[i]);//因为每次其实只和i-1比较,所以可以只存i-1行并与前边自 己比较,从而化简为一维数组(省空间,不省时间)
        }
    }
    
    cout<<dp[ve];
}

二.完全背包

与01背包很相似,只是每个物品有无限个并且可以选无限次。求最大价值。

(1)朴素版(三层循环O(n**3))太慢了需要优化

//#include<bits/stdc++.h>
//using namespace std;
//
//const int N1=1e4+5;
//const int N=1e7+5;
//int t[N1];
//int ve[N1];
//int dp[N1][N];
//int te,m;
//
//int main(){
//    cin>>te>>m;
//    for(int i=1;i<=m;i++){
//        cin>>t[i]>>ve[i];
//    }
//    
//    for(int i=1;i<=m;i++){
//        for(int j=1;j<=te;j++){
//            for(int k=0;k*t[i]<=j;k++){//枚举选k个从不选到k*t[i]<=j
//                
//                dp[i][j]=max(dp[i][j],dp[i-1][j-k*t[i]]+k*ve[i]);选k个的dp注意是i不是i-1;
//                
//            }
//
//        }
//    }
//    cout<<dp[m][te];
//    return 0;
//}

(2)完全背包的优化1 

#include<bits/stdc++.h>
using namespace std;

const int N=1e4+5;

int t[N];
int ve[N];
int dp[N][N];
int te,m;

int main(){
    cin>>te>>m;
    for(int i=1;i<=m;i++){
        cin>>t[i]>>ve[i];
    }
    
    for(int i=1;i<=m;i++){
        for(int j=1;j<=te;j++){
            
            dp[i][j]=dp[i][j];
            if(j>=t[i]){
                dp[i][j]=max(dp[i][j],dp[i][j-t[i]]+ve[i]);//与自己比较,只用将01背包中的i-1改为i即可
            }
        }
    }

    cout<<dp[m][te];
    return 0;
}

(3)完全背包的优化 2(优化为一维数组,更省空间)

#include<iostream>

using namespace std;
const int N = 1e7 + 5;
int n;
long long W[N], V[N], dp[N]; 
int main()
{
    int m, n;
    cin>>m>>n;
    for(int i = 1; i <= n; i++) cin>>V[i]>>W[i];

    
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            if(j >= V[i])
            {
                dp[j] = max(dp[j], dp[j-V[i]] + W[i]);
            }
        }
    }
    cout<<dp[m];
    return 0; 
}

三.多重背包问题

与完全背包和01背包很相似,只不过是每个物品有s件(或者说最多选s件),求可装最大价值。

(1)朴素版(还是三层循环,太慢)
//#include<bits/stdc++.h>
//using namespace std;
//const int N = 1e3 + 5;
//int dp[N][N];
//int num,ve;
//int n[N],vel[N],s[N];
//int main(){
//    cin>>num>>ve;
//    for(int i=1;i<=num;i++){
//        cin>>n[i]>>vel[i]>>s[i];
//    }
//    
//    for(int i=1;i<=num;i++){
//        for(int j=1;j<=ve;j++){
//            dp[i][j]=dp[i-1][j];
//            for(int k=1;k*n[i]<=j && k<=s[i];k++){
//                dp[i][j]=max(dp[i][j],dp[i-1][j-k*n[i]]+k*vel[i]);//解法类似于完全背包,多加一个循环条件
//            }
//        }
//    }    
//    cout<<dp[num][ve];
//}

(2)二分优化(重点)

三层循环总是太慢所以要优化。

s为数目,所以可以将其分解为2的k次方的和的形式,然后再将每一项单独打包,从而转换为纯粹意义上的 01背包。

如10个物品A可以拆分为1+2+4+3。3为最后剩下的

图示:A       AA       AAAA       AAA

每一份物品打包合一,变成一个新的大物品,则变换后为:

B     C      D     E 四个大物品,再进行01背包即可;利用这四个物品可以凑出任意在10以内的数字

//多重背包的优化+一维数组(滚动数组) 
//#include<iostream>     //O(N*log(S)*V)
//#include<cmath>
//using namespace std;
//
//const int N = 105, M = log(N);
//int W[N*M], V[N*M], S[N], dp[N];
//
//int main()
//{
//    int n, m;
//    cin>>n>>m;
//    int cnt = 0;
//    for(int i = 1; i <= n; i++)
//    {
//        int v, w, s;
//        cin>>v>>w>>s;
//        int k = 1;
//        while(k <= s)                //分组进行打包
//        {
//            V[++cnt] = k*v;
//            W[cnt] = k*w;
//            s -= k;
//            k *= 2;
//        }
//        if(s) //若有剩余就像3则继续打包
//        {
//            V[++cnt] = s*v;
//            W[cnt] = s*w;
//        }
//    }
//    
//    转化为纯粹的01背包
//    for(int i = 1; i <= cnt; i++)
//    {
//        for(int j = m; j >= V[i]; j--)
//        {
//            // dp[j] = dp[j]
//            dp[j] = max(dp[j], dp[j-V[i]] + W[i]);
//        }
//    }
//    cout<<dp[m];
//}

完~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值