背包总结
小蒟蒻其实一直都觉得自己背包学得特别烂,今天开始补学背包
一、01 背包
- 简介:顾名思义
01 背包就是01组成的背包对于每一件物品都只有两种状态,选或者不选。而每一件物品都有重量wei 和价值val. ,求如何装载使得不超过背包容量s 而又能获得最大价值.很明显在没学背包之前,我们会想到搜索int wei[MAXN],val[MAXN],s,n,ans=-1;//假设有n个物品,第i个重量为wei_i,价值为val_i,背包容量为s void dfs(int t,int wsum,int vsum)//wsum代表重量之和,vsum代表价值之和 { if(wsum>s) return; if(t==n+1) { ans=max(ans,vsum); return; } dfs(t+1,wsum,vsum); //不选 dfs(t+1,wsum+wei[i],vsum+val[i]); //选 }当然,这样的时间复杂度是
O(2^n) , 嗯···TLE 会制裁你的.
所以这个时候我们就用
-
基础启蒙(这一部分会快一点,因为重点不在这里)
$\ \ \ \ \ \ \ $很明显 $$ dp[i][j]=\left\{ \begin{aligned} dp[i-1][j](j<wei[i])\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ max(dp[i-1][j],dp[i-1][j-wei[i]]+val[i]) \end{aligned} \right. $$ 所以板子就可以打出来了(以[$P1048$ 采药](https://www.luogu.com.cn/problem/P1048)为例) $\mathcal{CODE} #include <iostream> #include <cstdio> using namespace std; int wei[105],pri[105]; int dp[105][1005]; int main() { int t,m; scanf("%d %d",&t,&m); for(int i=1;i<=m;i++) scanf("%d %d",&wei[i],&pri[i]); for(int i=1;i<=m;i++) { for(int j=1;j<=t;j++) { if(j>=w[i]) dp[i][j]=max(dp[i-1][j-wei[i]]+pri[i],dp[i-1][j]); else dp[i][j]=dp[i-1][j]; } } printf("%d",dp[m][t]); return 0; } -
滚动数组压空间
$$dp[j]=max(dp[j],dp[j-wei[i]]+val[i])$$ $\ \ \ \ \ \ \ $我们注意到,$dp[j-wei[i]]+val[i]$的计算是依赖上一层(之前的$dp$式)的前面计算结果的,因为我们的更新依赖于前面,所以我们不能顺序遍历,否则它的更新就会出错。此时正确做法应该是倒序遍历。 $\mathcal{CODE}$(还是以[$P1048$ 采药](https://www.luogu.com.cn/problem/P1048)为例) ```cpp #include <cstdio> #include <algorithm> using namespace std; int dp[1005],wei[1005],val[1005]; int main() { int m,n; scanf("%d %d",&m,&n); for(int i=1;i<=n;i++) scanf("%d %d",&wei[i],&val[i]); for(int i=1;i<=n;i++) { for(int j=m;j>=wei[i];j--) dp[j]=max(dp[j],dp[j-wei[i]]+val[i]); } printf("%d",dp[m]); return 0; } ``` ### 二、$01$背包进阶 $\ \ \ \ \ \ \ 01$背包的板子虽然小清新,但是它的题往往会很毒瘤,这里举几个例子 $\ \ \ \ \ \ \ \ $ 1. 有一定限制 $\ \ \ \ \ \ \ \ $[题目](https://vjudge.net/problem/HDU-3466#author=0)传送门(讲真我找了好久)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
struct node{
ll exp,sta;
}mos[105];
ll dp[805][805];
int main() {
ll n,m,k,s;
while(~scanf("%lld %lld %lld %lld",&n,&m,&k,&s))
{
memset(dp,127,sizeof(dp));
dp[0][0]=0;
for(ll i=1;i<=k;i++) scanf("%lld %lld",&mos[i].exp,&mos[i].sta);
for(ll i=1;i<=k;i++)
{
for(ll j=mos[i].exp;j<=n+500;j++)
{
for(ll l=1;l<=s;l++)
{
dp[j][l]=min(dp[j][l],dp[j-mos[i].exp][l-1]+mos[i].sta);
}
}
}
ll minn=1e9;
for(ll i=1;i<=s;i++)
{
for(int j=0;j<=500;j++) minn=min(minn,dp[n+j][i]);
}
if(minn<=m) printf("%lld\n",m-minn);
else printf("-1\n");
}
return 0;
}