背包问题

1. 01背包问题

有N件物品和一个容量是V的背包,每件物品只能使用一次,第i件物品的体积是 V i V_i Vi,价值是 W i W_i Wi,求将哪些物品放入背包,可以使这些物品的总体积不超过背包容量,且总价值最大。
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int f[N][N],v[N],w[N];
int n,m;
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 = 1 ;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]);//需要满足第i件物品的体积不大于总体积j
        }
    }
    cout << f[n][m];
    return 0;
    
    /* 待优化
    
    
    

    */
##
}

2. 完全背包问题

有N件物品和一个容量是V的背包,每件物品可以无限件使用,第i件物品的体积是 V i V_i Vi,价值是 W i W_i Wi,求将哪些物品放入背包,可以使这些物品的总体积不超过背包容量,且总价值最大。
在这里插入图片描述

  • 集合划分:f[i][j] 从前i种货币中选择,以最后一个不同点作为划分依据 对于第i种货币,可以选择0个、1个、2个····k个 也要满足k*w[i] <= j,因为要在背包能够装的下的情况下
  • 状态计算:f[i][j] = Max ( f[i-1][j] , f[i-1][j-v[i]] + w[i] , ···· , f[i-1][j-k*v[i]] + k*w[i] ),解释:f[i-1][j]表示从前i种货币中选,第i种货币选择0个,那么就等同于前i-1种货币中选择且总价值为j的方案数;f[i-1][j-w[i]]表示从前i种货币中选择,第i种货币选择1个,那么就等同于从前i-1种货币中选择且总价值为j-w[i](因为第i种货币选择了一个,所以总价值要减去1个第i种货币的价值)f[i-1][j-k*w[i]]表示从前i种货币中选择,第i种货币选择k个,那么就等同于从前i-1种货币中选择且总价值为j-k*w[i](因为第i种货币选择了k个,所以总价值要减去k个第i种货币的价值);
  • 优化:f[i][j] = Max ( f[i-1][j] , f[i-1][j-v[i]] + w[i] , ···· , f[i-1][j-k*v[i]] + k*w[i] ),
    对于f[i][j-v[i]] = Max ( f[i-1][j-v[i]] , f[i-1][j-2*v[i]] + w[i] , ···· , f[i-1][j-k*v[i]] + (k-1) * w[i] )
    f[i][j-v[i]] = Max ( f[i-1][j-v[i]] , f[i-1][j-2*v[i]] + w[i] , ···· , f[i-1][j-k*v[i]] + (k-1) * w[i] )Max(f[i-1][j-v[i]] + w[i] , ···· , f[i-1][j-k*v[i]] + k*w[i] ))整体少个w[i]
    f[i][j] = Max ( f[i-1][j] , f[i][j-v[i]] + w[i] )
#include<bits/stdc++.h>
using namespace std;
const int N =1010;
int f[N][N],v[N],w[N];
int main(){
    int n , m;
    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=1;j<=m;j++){
            f[i][j] = f[i-1][j];
            if(j>=v[i]) f[i][j] = max(f[i][j] , f[i][j-v[i]] + w[i]);
        }
    }
    cout << f[n][m];
    /*  优化
        
    
    
    
    
    */
}

3. 多重背包问题

有N件物品和一个容量是V的背包,第i种物品最多有 S i S_i Si件,第i种物品的体积是 V i V_i Vi,价值是 W i W_i Wi,求将哪些物品放入背包,可以使这些物品的总体积不超过背包容量,且总价值最大。
在这里插入图片描述
f[i][j] = Max ( f[i-1][j] , f[i-1][j-v[i]] + w[i] , ···· , f[i-1][j-k*v[i]] + k*w[i] ) ----k的取值范围:1、2、···、S[i]
朴素做法:O( n 3 n^3 n3)

#include<bits/stdc++.h>
using namespace std;
const int N = 110 ;
int n , m;
int v[N],w[N],s[N];
int f[N][N];
int main(){
    cin >> n >> m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>s[i];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int k=0;k<=s[i]&&k*v[i]<=j;k++){
                    f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k);
            }
        }
    }
    cout<<f[n][m];
}

优化做法:
对于f[i][j] = Max ( f[i-1][j] , f[i-1][j-v[i]] + w[i] , ···· , f[i-1][j-k*v[i]] + k*w[i] ) ----k的取值范围:1、2、···、S[i]
比如第i个物品的个数是 S i S_i Si=1023,也就是说对于第i个物品,可以选择0件、1件、···、1023件。那么可以将这1023件分为10组,第1组1件,第2组2件,第3组4件,依次按照2的幂分,第10组是 2 9 2^9 29=512件,这10组可以进行任意搭配(选用每组的个数最多是1)能够凑出来0~1023件,

  • 2 n 2^n 2n=2n-1+2n-1,2n-1=2n-2+2n-2,那么 2 n 2^n 2n=2n-1+2n-2+2n-2,那么 2 n 2^n 2n=2n-1+2n-2+2n-3+····+21+21 ,那2n-1-1 = 2n-1+2n-2+2n-3+····+21+20
  • 对于一般的S[i]: 2k<S,2k+1>S,那么2k+c = S (c<2k),那么1、2、4、···、2k、c就可以把0~S都凑出来 (每种只限最多选择一次)
    比如S=200,26=64<200,27>200,那么可以用1、2、4、8、16、32、64、73凑出0~200,因为1、2、4、···、64可以凑出0 ~ 127,那么再加73就可以凑出73 ~200,那么1、2、4、8、16、32、64、73就可以凑出0 ~200;
#include<bits/stdc++.h>
using namespace std;
const int N = 25000,M = 2010;
int n,m;
int v[N],w[N],s[N];
int f[N];
int main(){
    cin >> n >> m;
    int res = 0;
    for(int i=1;i<=n;i++){
            int k = 1;
            int a,b,s;
            cin >> a>>b>>s;
            while(k<=s){
                res ++;
                v[res] = a*k;
                w[res] = b*k;
                s -= k;
                k *= 2;
            }
            if(s>0){
                res ++;
                v[res] = a*s;
                w[res] = b*s;
            }
    }
    n = res;
    for(int i=1;i<=n;i++){
        for(int j=m;j>=v[i];j--){
            f[j] = max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m];
}

4.分组背包问题

有 N 组物品和一个容量是 V 的背包。每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
在这里插入图片描述
其中v[i,k] 代表第i组中的k件物品的体积 , w[i,k] 代表第i组中的k件物品的价值

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int v[N][N],w[N][N];
int f[N],s[N];
int n,m;
int main(){
    cin >> n>>m;
    for(int i = 1;i<=n;i++){
        cin >> s[i];
        for(int j=0;j<s[i];j++){
            cin >> v[i][j]>>w[i][j];
        }
    }
    for(int  i =1;i<=n;i++){
        for(int j = m;j>=0;j--){
            for(int k = 0;k<s[i];k++){
                if(v[i][k]<=j) f[j] = max(f[j] , f[j-v[i][k]] + w[i][k]);
            }
        }
    }
    cout << f[m];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值