背包问题-01背包
背包问题一直以来都是用来学习DP的小白鼠,对于背包问题我曾经在DFS中有举过栗子,那么这一次我们用状态转移的思想来学习一下01背包。
什么是01背包呢?说白了就是这个物品拿与不拿的问题。也就是说我面临决策的时候都有两个方案,拿与不拿。但是拿与不拿均对后面的结果会产生影响,所以说你无法一眼就看出来最优解(optimal solution,下面我将用OPT来概述)。那么我们只能够把所有的情况枚举出来然后进行选择,选择的过程中我们会有很多的问题,当然对于DP的问题就是重复计算,于是我们考虑如何才能够用状态转移把这个问题解决了。
首先推荐一个博客网站DP初学者必备,WOC太牛逼了!!!QAQ
那么我们先来搬运一道题目来揭开我们今天的帷幕。
题目描述
有 N件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
输入格式
第一行两个整数N,V,用空格隔开,分别表示物品数量和背包容积。下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
Sample Input
4 5
1 2
2 4
3 4
4 5
Sample Output
8
OK我们用两个方法来理解并解决这个问题。
首先,背包问题就是选与不选的问题,如果选了,那么背包容积就会减少,价值就会增加,所以咱们每选择一本书之后还得研究剩下空间的最大利用价值。
所以说,咱们应该从剩余的空间下手,剩余空间有多少,我们方可寻找最优解。
所以说咱们从空间剩余为1开始考虑(一共有6种空间剩余情况分别是0,1,2,3,4,5)这6种,而每种空间剩余会由上一个空间剩余决定,因此如果上一个剩余情况存在这个物品的存在,那么价值一定不会比目前选择这个物品的价值要小。
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
int f[1010][1010];
int n,m;
int v[10000],w[10000];
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=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]);
}
int res=0;
for(int i=0;i<=m;i++)res=max(res,f[n][i]);
cout<<res<<endl;
return 0;
}
说一下重复放置的问题,就目前的状态来说是不会出现重复放置的可能的,因为他会吧每一个东西的放与不放会放在不同的维度当中,用不同维度来进行转移。
所以不会有重复放置的问题。
但是如果用一维数组会有这样的情况。
下面把一维数组的代码给亮出来
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
int w[1010],v[1010];
int f[200000];
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)cin>>w[i]>>v[i];
for(int i=n;i>=0;i--)
for(int j=m;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+v[i]);
cout<<f[m]<<endl;
}
你会发现我这个代码的循环是反序的因为,如果我是正序的话会有可能把之前的物品重复放置的。如果你反序走的话,每一次更新的都是依据之前没放这个物体的状态转移过来的。所以不会有重复的问题。那么如果你更新的依据是更新后的,那么很有可能就会把这个物品重复放置!
By-轮月
来源:CSDN
作者:轮月
链接:https://blog.csdn.net/qq_35339563/article/details/103580729