01背包问题

01背包问题

1.01背包问题介绍

01背包是指有N件物品和一个容量为V的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

01背包中的“01”是指每个物品只能选或不选,也就是选1次和选0次。

2.01背包问题例题

题目描述

一个旅行者有一个最多能装 \(V\) 公斤的背包,现在有 \(N\) 件物品,它们的重量分别是 \(W_1,W_2,...,W_n\),它们的价值分别为\(V_1,V_2,...,V_n\),求旅行者能获得最大总价值。

输入

第一行:两个整数,M(背包容量,\(M<=200\))和 \(N\)(物品数量,\(N<=30\));

\(2..N+1\) 行:每行二个整数 \(W_i,V_i\) ,表示每个物品的重量和价值。

输出

仅一行,一个数,表示最大总价值。

输入样例

10 4
2 1
3 3
4 5
7 9

输出样例

12

注:本题来自AcWing题库第2题

3.01背包问题思路

Dp一般分为两个思考部分

第一个是状态表示,也就是 \(f\) 数组。

在01背包问题中,\(f[i][j]\) 代表从前 \(i\) 个物品中选且总重量不超过 \(j\) 的选法集合,它的值是所有算法集合的最大值。

第二个是状态计算,也就是状态转移方程。

前一阶段的终点就是后一阶段的起点,对前一阶段的状态作出某种决策,产生后一阶段的状态,这种关系描述了由k阶段到k+1阶段状态的演变规律,称为状态转移方程。

大致是这个意思,具体可能有错误

01背包问题可以把 \(f[i][j]\) 分成含 \(i\) 和不含 \(i\) 的集合,然后取最大值。所以动态转移方程为:

\[f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]) \]

\(f[i-1][j]\):在这个集合里取不含 \(i\) 的小集合。

\(f[i-1][j-v[i]]+w[i]\):用Y总的话来说就是“曲线救国”。要想直接求含 \(i\) 的集合很难,所以我们可以先将第 \(i\) 个物品的重量减去,再在不含 \(i\) 的集合里找,最后加上 \(i\) 的权重就好了。

注:在集合的划分上一般要做到不重不漏,只有个别情况不用

4.01背包问题代码

#include<iostream>
#define MAXN 1005
using namespace std;
int v[MAXN],w[MAXN],f[MAXN][MAXN];
int main(){
    int n,m;
    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=0;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]);
        }
    }
    cout<<f[n][m];
    return 0;
}

5.01背包问题优化

我们通过观察可以得出,\(f\) 数组的第一维只用到了 \(i-1\),所以我们可以用滚动数组存储,优化掉第一维。

状态转移方程:

\[f[j]=max(f[j],f[j-v[i]]+w[i]) \]

6.代码(优化)

#include<iostream>
using namespace std;
const int MAXN=1005;
int v[MAXN],w[MAXN],f[MAXN];
int main(){
    int n,m;
    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=m;j>=v[i];j--)
            f[j]=max(f[j],f[j-v[i]]+w[i]);
    cout<<f[m];
    return 0;
}

完awa~

如果觉得好就点个赞吧,您的支持就是本蒟蒻最大的动力。

posted @ 2022-07-17 22:56  Rainforests  阅读(222)  评论(0编辑  收藏  举报