其他分享
首页 > 其他分享> > 背包问题

背包问题

作者:互联网

背包问题

体面一般是给你n个物品,体积为m的背包,每个物品的体积为v[i],价值w[i],求这个背包可以装的最大价值。

1.dp问题的分析思路。

image-20220722185250914

1.状态表示有表示当前状态下的集合里面某个属性的最优解。属性一般就是最大值,最小值,数量。

2.状态计算就是考虑如何把当前集合划分为更小的子集,然后从前面已经求解的结果计算出来。如背包问题集合划分就是考虑选不选第i个物品。要求对集合的划分不应该漏掉某种情况。

所以动态规划的解题思路应该是先定义状态(状态表示),然后对这个状态所定义集合划分,然后在由集合的性质来从前面已经求解的状态来求解。

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] [j]状态都是从f[i-1]行优化过来。所以他也可以优化从一维的。

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

但是注意这样枚举体积的时候就应该逆序枚举,因为这样我求每一个状态f[i]的时候他前面的都还是没有更新的(也就是前面状态都还是上一行的)。

2.完全背包

完全背包的状态转移方程推导

image-20220722194232644

由这个可以推到完全背包的状态转移方程是

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

他也是可以优化的。很明显f[i] [j]的推导只与他这一行有关系,是从f[i]行转移过来的,所以是转移f[i] [j]时只需要这一行更新的。所以将他化成一维的的时候时需要更新这一行。所以我枚举体积的时候应该顺序枚举。

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

3.多重背包

  1. 二进制优化 (优化成了01背包)

#include<bits/stdc++.h>
using namespace std;
const int N=25000,M=2010;
int n,m;
int v[N],w[N],dp[M];
int main(){
cin>>n>>m;
int cnt=0;
for(int i=1;i<=n;i++){
int a,b,s;
cin>>a>>b>>s;
int k=1;
while(k<=s){
v[++cnt]=a*k;
w[cnt]=b*k;
s-=k;
k*=2;
}
if(s>0){
v[++cnt]=a*s;
w[cnt]=b*s;
}
}
for(int i=1;i<=cnt;i++){
for(int j=m;j>=v[i];j--){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<dp[m]<<endl;
return 0;
}

4.分组背包

就是每组只能选一个。

image-20220722203634809

那很明显状态转移就是要么不选这一组,要么就在这一组中选择第k个

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

这个复杂度很明显时O(n^3)的。

5.到达型背包

这主要就是看某个状态可不可以到达,比如洛谷的砝码称重,就是看某个状态可不可达。还有音量调节。

一般就是选不选这个物品,但是的注意是都从上一行优化过来的吗?都是从一侧优化过来的吗?

$$
dp[i][j]=dp[i-1][j]||dp[i-1][j-v[i]]
$$

这两个状态有一个是可达的dp[i] [j]就是可达的。

疑问:背包问题对于dp[i]一定使装满的吗?不一定呀,那为什么洛谷p2918可以这样写?不懂

标签:状态,cnt,背包,int,max,问题,dp
来源: https://www.cnblogs.com/silky----player/p/16519243.html