挑战性题目DSCT601:背包问题
问题描述
有一个容量为
V
V
V的背包,要求往背包中装入价值尽可能多的物品。这些物品分别有两个属性:体积
w
w
w和价值
v
v
v,且每种物品至多有一个,背包可以不被装满,求装入后的最大价值。
输入输出格式:
第一行为两个整数
N
、
V
N、V
N、V,用空格隔开,表示背包的容量
V
V
V与物品的数量
N
N
N。接下来
N
N
N行每行由两个整数
w
i
w_i
wi?与
v
i
v_i
vi?组成,用空格隔开,表示物品i的体积和价值。请以printf() 方式输出最大价值即可。
约定
0
<
N
,
V
≤
1000000
0 < N, V ≤ 1000000
0<N,V≤1000000,且
0
<
w
i
,
v
i
≤
4294967296
0 < wi,vi ≤ 4294967296
0<wi,vi≤4294967296
题解
讲个笑话:开学一个月,挑战题第一道是数位dp;快期末了,挑战题最后一道是背包问题。而这门课没有讲动态规划😅
还有个更好笑的,
N
×
V
=
1
0
12
N\times V=10^{12}
N×V=1012的背包问题😅
基础的01背包问题,dp[i][j] 表示枚举到第
i
i
i个物品且背包装下了体积为
j
j
j的物品时物品的最大价值。于是我们每次枚举物品,尝试用新的物品去更新每个体积的最大价值,那么转移方程就是 dp[i][j]=max(dp[i][j],dp[i-1][j-vi]+wi)
可以发现,每次转移时我们仅调用了下标为i-1 的数组,因此可以采用滚动数组的办法,省掉一维的空间。需要注意因为每次更新仅会修改j更大的节点的值,所以我们不能正序访问,而需要倒序访问,否则该问题会变为完全背包问题。
这样,01背包的理论复杂度为
O
(
N
V
)
O\left(NV\right)
O(NV),看起来对于本题
10
6
{10}^6
106的数据规模并不够用,但是由于物品的体积
v
i
v_i
vi?范围较大,倘若数据随机生成,则容量落在
10
6
{10}^6
106范围内的物品的期望个数为
10
6
×
10
6
4294967296
≈
233
{10}^6\times\frac{{10}^6}{4294967296}\approx233
106×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]);
}
|