既然选择了远方,便只顾风雨兼行|

H_W_Y

园龄:1年11个月粉丝:28关注:15

2023-09-18 21:00阅读: 55评论: 0推荐: 0

P3188 [HNOI2007] 梦幻岛宝珠-题解

20230918

P3188 [HNOI2007] 梦幻岛宝珠

Statement

01背包, n100,但是容量 m230

物体的体积可以写成 a×2b(a10,b30)

Solution

发现 W 太大了,而 a,b 又很小,

那我们考虑对背包进行分组。

fi,j 维护第 i 组容量为 j×2i 的答案。

每一组就直接用普通背包做就可以了。

现在考虑如何合并?

2i1 转移到 2i 时,我们同样用 fi,j 表示 20i 位, j×2i 时的答案。

我们考虑可以从 2i1 那里拿一些上来,

假设拿上来 k×2i ,于是:

fi,j=maxk=0j{fi1,k×2+[W&(1<<(i1))]+fi,jk}

其中 [W&(1<<(i1))] 是如果 W 里面本来可以有 2i1

我就可以多拿上来一个。

于是这道题就做完了。

时间复杂度 O(T×len×1000×1000)

/*https://www.cnblogs.com/hwy0622/p/17713057.html*/
#include <bits/stdc++.h>
using namespace std;
#define int long long

int n,W,len=0,w;
struct node{
  int a,b,val;
  bool operator <(const node &rhs) const{
    return b<rhs.b;
  }
}a[2005];
int f[50][1005];

signed main(){
  /*2023.9.18 H_W_Y P3188 [HNOI2007] 梦幻岛宝珠 分组背包*/ 
  while(scanf("%lld%lld",&n,&W)){
  	if(n==-1||W==-1) break;
  	for(int i=1;i<=n;i++){
  	  scanf("%lld%lld",&w,&a[i].val);
	  a[i].a=w/(w&(-w));  
	  a[i].b=0;
	  for(w=(w&(-w))>>1;w;w>>=1) a[i].b++;
	}
  	memset(f,0,sizeof(f));
	for(int j=0;j<=1000;j++) f[0][j]=0;
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)//直接枚举 
	  for(int j=1000;j>=a[i].a;j--)
	    f[a[i].b][j]=max(f[a[i].b][j],f[a[i].b][j-a[i].a]+1ll*a[i].val);
	len=0;
	for(int s=W>>1;s;s>>=1) len++;
	for(int i=1;i<=len;i++){
	  for(int j=1000;j>=0;j--)
	    for(int k=0;k<=j;k++)
	      f[i][j]=max(f[i][j],f[i-1][min(1000ll,(k<<1)|((W&(1<<(i-1)))!=0))]+f[i][j-k]);	//必须要加上!=0	
	}
	printf("%lld\n",f[len][1]);
  }
  return 0;
}

Conclusion

这道题有特殊性质,我们可以考虑把它分组。

在合并的时候注意上一个带上来的贡献……

思路还是挺妙的~

本文作者:H_W_Y

本文链接:https://www.cnblogs.com/H-W-Y/p/17713057.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   H_W_Y  阅读(55)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起