luogu 3188 [HNOI2007]梦幻岛宝珠

LINK:梦幻岛宝珠

时隔多日 我再次挑战这道题。还是以失败告终。

我觉得这一道背包真的有点难度 这是一个数量较少 但是价值和体积较大的背包。

通常的01背包 要不就是体积小 要么是价值小 但这道题给了价值和体积都大 说明必然有其他重要的条件。

那就是体积为(a*2^b) (aleq 10,bleq 30)

只有这一个有利用价值的东西 我们无奈的对这个东西进行dp 对b进行分组 然后各个组进行dp.

关键我们如何把这些组给合起来。我想了很久 也翻了好几篇题解。

我终于明白为什么了。这个题 对我来说感觉很困难。

我们要得到容量为m的答案 还是考虑按位枚举背包的大小。

dp[i][j] 表示考虑到了第i为 此时我们背包的容量为(jcdot 2^i+m&((1<<i)-1))容量为这么多时的价值。

之所以是这个因为我们是从小到大来记录到底拿了多少 考虑到了第i位 对于第i位 我们可能当前没有这么多钱 但是之后高位可能有所以当前就需要存一下这个状态而后面的是我们应该有了这么多的钱用来买了。

所以这样dp一下 可以发现对于j的枚举 最多1000 也可以记一个总量来辅助转移。

这样转移可以保证的是 这是类似于背包的转移 且也不需要开过大的空间。

可以证明这是正确的。

//#include<bitsstdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define ld long double
#define pb push_back
#define get(x) x=read()
#define gtc(x) scanf("%s",a+1)
#define gt(x) scanf("%d",&x)
#define put(x) printf("%d
",x)
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int> 
#define F first
#define S second
#define mk make_pair
#define mod 1000000009
#define RE register
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
	return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
	RE int x=0,f=1;char ch=getc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
	return x*f;
}
const int MAXN=1010,maxn=33;
int n,m,mx;
vector<int>w[maxn],v[maxn];
int f[maxn][MAXN],c[maxn];
int main()
{
	freopen("1.in","r",stdin);
	while(1)
	{
		get(n);get(m);mx=0;
		memset(f,0,sizeof(f));
		memset(c,0,sizeof(c));
		memset(w,0,sizeof(w));
		memset(v,0,sizeof(v));
		if(n==-1&&m==-1)break;
		rep(1,n,i)
		{
			int x,y;
			get(x);get(y);
			int cnt=0;
			while(1)
			{
				if(x&1)break;
				x=x>>1;++cnt;
			}
			w[cnt].pb(x);v[cnt].pb(y);
			mx=max(mx,cnt);c[cnt]+=x;
		}
		rep(0,mx,i)
		{
			rep(0,(int)(w[i].size())-1,j)
			{
				for(int k=c[i];k>=w[i][j];--k)
					f[i][k]=max(f[i][k],f[i][k-w[i][j]]+v[i][j]);
			}
		}
		int len=0;
		while(m>>len)++len;--len;
		//cout<<len<<endl;
		rep(1,len,i)
		{
			c[i]+=(c[i-1]+1)/2;
			//cout<<c[i]<<endl;
			for(int j=c[i];j>=0;--j)
				for(int k=0;k<=j;++k)
					f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min(c[i-1],(k<<1)+((m>>(i-1))&1))]);
		}
		put(f[len][1]);
	}
	return 0;
}

虽然写完了 但是这道题 还需要再细细揣摩其中dp合并的思想。

原文地址:https://www.cnblogs.com/chdy/p/12514600.html