背包问题

(1)01背包

有N件物品和一个容量为V的背包。第i件物品的价格(即体积,下同)是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

解题思想:对第i件物品选不选的问题。

状态方程:f[i][j] = f[i - 1][j]  //第i件没有选

                  f[i][j] = max(f[i - 1][j - w[i]] + c[i], f[i - 1][j]) //第i件被选

最开始不理解这两个方程是什么意思,为什么要用这两个方程来表示,后来渐渐明白是用二维数组来表示所有的情况,j - w[i]表示当j满足j > w[i]时,前i - 1 的价值加上 i 的价值。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 1010;
int f[maxn][maxn];

int main()
{
    int t, n, c[maxn], v[maxn];
    cin >> t >> n;
    for(int i = 1; i <= n; i++)
        cin >> c[i] >> v[i];
    memset(f, 0, sizeof(f));
    for(int i = 1; i <= n; i++){
        for(int j = 0; j <= t; j++)
            f[i][j] = f[i - 1][j];  //第i件没有选的价值
        for(int j = 0; j <= t - c[i]; j++) //对所有减去第i件时间的穷举
            f[i][j] = max(f[i - 1][j], f[i - 1][j - c[i]] + v[i]); //第i件被选的价值
    }
    int sum = 0;
    for(int i = 1; i <= n; i++)
        sum = max(sum, f[n][i]); //找到最大值
    cout << sum << endl;
    return 0;
}

简化一下也可以用一维数组来求解,从背包最大的容量开始一点一点的存储,最后的那一个值就是最大值

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 1010;
int f[maxn];

int main()
{
    int m, n, v[maxn], c[maxn];
    cin >> m >> n;
    for(int i = 1; i <=n; i++)
        cin >> v[i] >> c[i];
    for(int i = 1; i <= n; i++){
        for(int j = m; j >= v[i]; j--) //注意逆序
            f[j] = max(f[j], f[j - v[i]] + c[i]);
    }
    cout << f[m] << endl;
    return 0;
}

(2)完全背包

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

这由01背包的两种选择变为了选择,不选择和选择多少个三个选择。

将完全背包转化为01背包,进行拆分,把重复可用的想成相同的价值不同的物品进行叠加。

状态转移方程:f[i][v]=max(f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v)

 差分成01背包优化: f[j] = max(f[j],f[j-c[i]]+w[i])

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 1010;

int n, m, c[maxn], v[maxn];
int main()
{
    int f[maxn];
    cin >> m >> n;
    for(int i = 1; i <= n; i++)
        cin >> c[i] >> v[i];
    memset(f, 0, sizeof(f));  //注意清零,不清零会任意赋值出现错误
    for(int i = 1; i <= n; i++){
        for(int j = c[i]; j <= m; j++)
            f[j] = max(f[j - c[i]] + v[i], f[j]);
    }
    cout << f[m] << endl;
    return 0;
}

 

(3)多重背包

有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

状态转移方程:f[i][v]=max(f[i-1][v-k*w[i]]+ k*c[i]|0<=k<=n[i])

一道例题↓_↓

庆功会

【问题描述】

为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。

【输入格式】

第一行二个数n(n<=500),m(m<=6000),其中n代表希望购买的奖品的种数,m表示拨款金额。 接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和购买的数量(买0件到s件均可),其中v<=100,w<=1000,s<=10。

【输出格式】

第一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。

【输入样例】

5 1000

80 20 4

40 50 9

30 50 7

40 30 6

20 20 1

【输出样例】

1040

 

未优化算法,在多重背包基础上再嵌套一个对次数的嵌套循环

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 6010;

int n, m, c[maxn], v[maxn], x[maxn];
int main()
{
    int f[maxn];
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> c[i] >> v[i] >> x[i];
    memset(f, 0, sizeof(f));
    for(int i = 1; i <= n; i++){
        for(int j = m; j >= 0; j--){
            for(int k = 0; k <= x[i]; k++){  //对k的次数进行选择
                if(j - k * c[i] < 0)  break;
                f[j] = max(f[j - k * c[i]] + k * v[i], f[j]);
            }
        }
    }
    cout << f[m] << endl;
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值