一道bitset优化背包的练习题

4 篇文章 0 订阅
3 篇文章 0 订阅

n n n 种物品,第 i 种物品有 c i ci ci 个,体积为 v i vi vi
给定整数集合 S S S,从这 n 种物品中选出 k ( k ∈ S ) k(k ∈ S ) k(kS)种物品,每种物品选任意正整数
个,问可以得到哪些小于 L L L 的总体积值。
所有输入数据小于等于2000

首先 f [ i ] [ j ] f[i][j] f[i][j]表示选了 j j j种物品,能否获得 i i i的体积,然后可以 b i t s e t bitset bitset优化到 O ( n 4 / w ) O(n^4/w) O(n4/w)

然后考虑这个在转移中,假设当前枚举到第 i i i种物品,对于一个 f [ j ] f[j] f[j],能够转移来的 f [ k ] f[k] f[k]间相隔的距离是相同的,是 v [ i ] v[i] v[i],所以可以把之前的可以转移到的体积根据对 v [ i ] v[i] v[i]取模的值分类,然后转移就是当前这个 f [ i ] f[i] f[i]或上之前的 f [ i − 1 ] < < 1 , f [ i − 2 ] < < 1 , . . . , f [ i − c ] < < 1 f[i-1]<<1,f[i-2]<<1,...,f[i-c]<<1 f[i1]<<1,f[i2]<<1,...,f[ic]<<1,然后此时复杂度还是很高,但是仔细观察可以发现,如果将 f [ i ] f[i] f[i] c c c个预处理出来,就可以把每个转移看成一个前缀和一个后缀或起来.
在这里插入图片描述
这样时间复杂度 O ( n 3 / w ) O(n^3/w) O(n3/w)

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char c=getchar();int t=0,f=1;
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
const int maxn=2005;
bitset<maxn> dp[maxn],pre[maxn],suf[maxn],ans;
int n,l,m,cnt,c,v;
bitset<maxn> get(int l,int r){//这里就是dp转移
	if(l%c==0)return pre[r];
	else return suf[l]|pre[r];
}
int main(){
	n=read(),l=read();dp[0][0]=1;
	for(int i=1;i<=n;i++){
		c=read(),v=read();
		for(int j=0;j<v;j++){
			for(int k=0;k*v+j<=l;k++){pre[k]=suf[k]=dp[k*v+j]<<1;cnt=k;}
			for(int k=cnt-1;k>=0;k--)if((k+1)%c)suf[k]|=suf[k+1];
			for(int k=1;k<=cnt;k++)if(k%c)pre[k]|=pre[k-1];
			for(int k=1;k<=cnt;k++)dp[k*v+j]|=get(max(0,k-c),k-1);
		}
	}
	m=read();
	for(int i=1;i<=m;i++){
		int x=read();ans[x]=1;
	}
	for(int i=1;i<=l;i++)if((dp[i]&ans).count())printf("%d ",i);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值