挑战性题目DSCT601:背包问题
问题描述
有一个容量为VVV的背包,要求往背包中装入价值尽可能多的物品。这些物品分别有两个属性:体积www和价值vvv,且每种物品至多有一个,背包可以不被装满,求装入后的最大价值。
输入输出格式:
第一行为两个整数N、VN、VN、V,用空格隔开,表示背包的容量VVV与物品的数量NNN。接下来NNN行每行由两个整数wiw_iwi与viv_ivi组成,用空格隔开,表示物品i的体积和价值。请以printf()方式输出最大价值即可。
约定0<N,V≤10000000 < N, V ≤ 10000000<N,V≤1000000,且0<wi,vi≤42949672960 < wi,vi ≤ 42949672960<wi,vi≤4294967296
题解
讲个笑话:开学一个月,挑战题第一道是数位dp;快期末了,挑战题最后一道是背包问题。而这门课没有讲动态规划😅
还有个更好笑的,N×V=1012N\times V=10^{12}N×V=1012的背包问题😅
基础的01背包问题,dp[i][j]表示枚举到第iii个物品且背包装下了体积为jjj的物品时物品的最大价值。于是我们每次枚举物品,尝试用新的物品去更新每个体积的最大价值,那么转移方程就是
dp[i][j]=max(dp[i][j],dp[i-1][j-vi]+wi)
可以发现,每次转移时我们仅调用了下标为i-1的数组,因此可以采用滚动数组的办法,省掉一维的空间。需要注意因为每次更新仅会修改j更大的节点的值,所以我们不能正序访问,而需要倒序访问,否则该问题会变为完全背包问题。
这样,01背包的理论复杂度为O(NV)O\left(NV\right)O(NV),看起来对于本题106{10}^6106的数据规模并不够用,但是由于物品的体积viv_ivi范围较大,倘若数据随机生成,则容量落在106{10}^6106范围内的物品的期望个数为106×1064294967296≈233{10}^6\times\frac{{10}^6}{4294967296}\approx233106×4294967296106≈233,所以只要做好判断,就可以AC此题。
代码
#pragma GCC optimize(2)
#include<cstdio>
#include<ctype.h>
#define LL long long
using namespace std;
const int M=1e6;
int N,V,w,v;
LL dp[M+5],ans,r;
char c;
inline char nc()
{
static const int buflen=1e6;
static char buf[buflen],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,buflen,stdin),p1==p2)?EOF:*p1++;
}
LL read(){for(r=0;!isdigit(c);c=nc());for(;isdigit(c);c=nc())r=(r<<1)+(r<<3)+c-'0';return r;}
void out(LL x){if(x>9)out(x/10);putchar(x%10+'0');}
int main(int argc,char* argv[])
{
freopen(argv[1],"r",stdin);
N=read(),V=read();
for(int i=1;i<=N;++i)
{
w=read(),v=read();
for(int j=V;j>=w;--j)
if(dp[j]<dp[j-w]+v)dp[j]=dp[j-w]+v;
}
out(dp[V]);
}

本文介绍了一种解决背包问题的方法,使用动态规划中的01背包算法,并通过滚动数组优化空间复杂度。针对大规模数据,文章还提供了高效的实现技巧。
697

被折叠的 条评论
为什么被折叠?



