基础背包问题(01,完全,多重)

背包问题

\(01\)背包

题目描述:有\(n\)件物品和一个容量为\(v\)的背包,每个物品放进背包需要占用\(S_i\)的空间,可以得到\(W_i\)的价值,放入一些物品使得价值最大,求最大值

分析:

状态设计:

  1. 明显的发现这个问题与\(v,S_i,W_i\)有关,于是可以将空间开出一维,价值用\(F\)数组的值表示,设\(F_{j}\)表示恰好占用\(j\)空间的最大价值
  2. 因为\(DP\)要从小问题得到大问题的解,由于物品排列的先后顺序不会影响答案,就可以加多一维\(i\),表示\(F_{i,j}\)\(i\)个物品中,占用空间恰好为\(j\)的最大价值

转移方程:

  1. 假设当前我们要求\(F_{i,j}\),那么对于第\(i\)个物品,就有取和不取,于是就有$$F_{i,j}=\left{
    \begin{aligned}
    F_{i-1,j} (不取)(0\leqslant j \leqslant v)\
    F_{i-1,j-S_i}+W_i (取)(s_i\leqslant j \leqslant v)\
    \end{aligned}
    \right.$$
  2. 于是枚举\(i,j\)即可解决问题

时空优化:

  1. 时间上复杂度为\(O(nv)\),无法优化
  2. 空间上复杂度为\(O(nv)\),发现\(F_{i,j}\)只与\(F_{i-1,j(0\leqslant j \leqslant v)}\)有关,于是可以把\(i\)用滚动数组滚掉。

\(F\)初始化:

  1. 明显的全部为\(0\)即可

代码实现(优化):

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;

void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
int old[1010],now[1010];
struct node { int s,w; }a[1010];
int n,v;
int main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	read(n),read(v);
	for (int i=1;i<=n;i++) read(a[i].s),read(a[i].w);
	memset(old,0,sizeof(old));
	memset(now,0,sizeof(now));
	for (int i=1;i<=n;i++)
	{
		for (int j=0;j<=v;j++)
		{
			now[j]=old[j];
			if (j>=a[i].s)
			{
				now[j]=max(now[j],old[j-a[i].s]+a[i].w);
			}
		}
		for (int j=0;j<=v;j++)
			old[j]=now[j];
	}
	int ans=0;
	for (int i=0;i<=v;i++) ans=max(ans,old[i]);
	printf("%d",ans);
//	fclose(stdin);fclose(stdout);
	return 0;
}

例题:采药

完全背包

题目描述:题目描述:有\(n\)种物品和一个容量为\(v\)的背包,每种物品放一个进背包需要占用\(S_i\)的空间,可以得到\(W_i\)的价值,放入一些物品使得价值最大,每种物品可以有无数个(空间大小要满足条件),求最大值

分析:

状态设计:

  1. \(01\)背包同理,设\(F_{i,j}\)表示前\(i\)种物品恰好占用\(j\)空间物品的最大价值

转移方程:

  1. 假设当前选择到第\(i\)种物品,要选择\(k\)个,那么\(F_{i,j}\)可能由\(F_{i-1,j-k*S_i}\),或者直接继承\(F_{i-1,j}\)

时空优化:

  1. 原时间复杂度为\(O(nv\sum_{i=1}^n(\frac{v}{S_i}))\),空间为\(O(nv)\)
  2. 现在考虑优化,假设有两种状态分别为\(F_{i,j-S_i},F_{i,j}\),那我们在\(DP\),发现其实它们只有\(F_{i,j-S_i}\)的差别,于是考虑减少次数
  3. 发现\(F_{i,j-S_i}\)已经把除了\(F_{i,j-S_i}\)之外的所有\(F_{i,j}\)要考虑的状态都考虑了,所以我们发现可以把\(k\)去掉,从小到达枚举\(j\),每次只需转移\(F_{i,j}=F_{i,j-S_i}\)。注意要考虑不取的情况,即\(F_{i,j}=F_{i-1,j}\)
  4. 在加上一个滚动数组把\(i\)滚掉,时间复杂度\(O(nv)\),空间复杂度\(O(v)\)

\(F\)初始化:

  1. 明显的全部为\(0\)即可

代码实现(优化):

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
typedef long long ll;

void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
int n,v;
struct node { int s,w; }a[10010];
int f[10000010];
signed main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	read(n),read(v);
	for (int i=1;i<=n;i++) read(a[i].s),read(a[i].w);
	for (int i=1;i<=n;i++)
	{
		for (int j=0;j<=v-a[i].s;j++)
			f[j+a[i].s]=max(f[j+a[i].s],f[j]+a[i].w);
	}
	int mmax=0;
	for (int i=0;i<=v;i++)
		mmax=max(f[i],mmax);
	printf("%lld",mmax);
	//	fclose(stdin);fclose(stdout);
	return 0;
}

例题:疯狂的采药

多重背包

题目描述:有\(n\)种物品和一个容量为\(v\)的背包,每种物品放一个进背包需要占用\(S_i\)的空间,可以得到\(W_i\)的价值,放入一些物品使得价值最大,每种物品有\(T_i\)个(空间大小要满足条件),求最大值

分析:

状态设计:

  1. 同完全背包,设\(F_{i,j}\)表示前\(i\)种物品恰好占用\(j\)空间物品的最大价值

转移方程:

  1. 这个问题可以看作,是一个完全背包,只不过把有每种\(v/S_i\)个,改成了\(T_i\),可以按完全背包朴素的方法做,\(F_{i,j}\)可能由\(F_{i-1,j-k*T_i}\),或者直接继承\(F_{i-1,j}\)

时空优化:

  1. 原时间复杂度为\(O(nv\sum_{i=1}^n(\frac{v}{S_i}))\),空间为\(O(nv)\)
  2. 考虑\(k\)的问题,其实最多就\(T_i\)个,考虑到用几个\(2^0\)~\(2^t\)可以表示出\(2^{t+1}-1\)里的所有数,所以将第\(i\)种物品变成\(\log_2T_i+2\)个物品,设\(\log_2T_i=g\)\(1\) ~ \(g-1\)是体积为\(2^t\)价值为\(W_i*2^t\)的物品,\(g\)个物品是体积为\(T_i-2^g+1\)价值为\(W_i*(T_i-2^g+1)\)的物品,于是这样就可以像\(01\)背包一样做了
  3. 滚掉\(i\)
  4. 时间复杂度\(O(nv\sum_{i=1}^n\log_2(\frac{v}{S_i}))\),空间复杂度\(O(v)\)

\(F\)初始化:

  1. 明显的全部为\(0\)即可

代码实现(优化):

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;

void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
int f[101000];
struct node { int s,w; }a[1010*63];
int n,v;
int main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	int len;read(len),read(v);
	for (int i=1;i<=len;i++)
	{
		int x,w,t;read(x),read(w),read(t);
		int d=1;
		while (d<=t)
		{
			n++;
			a[n].s=d*x;
			a[n].w=d*w;
			d*=2;
		}
		a[n].s=x*(t-d/2+1);
		a[n].w=w*(t-d/2+1);
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=v;j>=a[i].s;j--)
		{
			f[j]=max(f[j],f[j-a[i].s]+a[i].w);
		}
	}
	int ans=0;
	for (int i=0;i<=v;i++) ans=max(ans,f[i]);
	printf("%d",ans);
//	fclose(stdin);fclose(stdout);
	return 0;
}

例题:宝物筛选

posted @ 2022-01-24 23:01  WBWYX  阅读(37)  评论(0)    收藏  举报