挑战性题目DSCT601:背包问题

挑战性题目DSCT601:背包问题

问题描述

有一个容量为 V V V的背包,要求往背包中装入价值尽可能多的物品。这些物品分别有两个属性:体积 w w w和价值 v v v,且每种物品至多有一个,背包可以不被装满,求装入后的最大价值。

输入输出格式:

第一行为两个整数 N 、 V N、V NV,用空格隔开,表示背包的容量 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,V1000000,且 0 < w i , v i ≤ 4294967296 0 < wi,vi ≤ 4294967296 0<wi,vi4294967296

题解

讲个笑话:开学一个月,挑战题第一道是数位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×4294967296106233,所以只要做好判断,就可以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]);
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值