【bzoj1190】[HNOI2007]梦幻岛宝珠 分层背包dp

题目描述

给你N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大为,并输出最大的总价值。数据范围:N<=100;W<=2^30,并且保证每颗宝石的重量符合a*2^b(a<=10;b<=30)

输入

输入文件中包含多组数据。每组数据的格式如下:第一行是两个正整数n和W,1≤n≤100,1≤W≤2^30,分别表示宝石的数目和最多能带走的宝石重量。接下来的n行,每行有两个正整数weighti和valuei,1≤weighti≤2^30, 0≤valuei≤2^30,分别表示第i颗宝石的重量和价值,且保证weighti能写成a*2^b(1≤a≤10,0≤b≤30)的形式。同一行的两个正整数之间用空格隔开。最后一组数据的后面有两个-1,表示文件的结束。这两个-1并不代表一组数据,你不需对这组数据输出结果。并且输入文件中数据的组数不超过20。

输出

对于输入的每组数据,输出一个整数C,表示小P最多能带走的宝石的总价值。每个结果整数C单独占一行,且保证C不会超过2^30。

样例输入

4 10
8 9
5 8
4 6
2 5
-1 -1

样例输出

14



题解

分层背包dp

本题显然是一个01背包问题,然而W有$2^30$之大,显然无法直接存储。

注意到题目中“保证weighti能写成$a*2^b$的形式”这个关键条件,因此可以由此入手。

设$f[i][j]$表示所选容量为$j*2^i$时得到的最大价值。那么在转移时仅考虑同一层(相同的$2^b$的物品)的转移,就是普通的01背包。

然后是跨层的转换,即将$f[i][j]$转换为$f[i+1][k]$。考虑到以后的位对当前位都不会有影响,因此只需要考虑多少的$2^{i+1}$能够满足$j*2^i$。显然$k=\lceil\frac{j-w的当前位}2\rceil$

最后的答案可以直接从$f[31][0]$拿到,因为第$31$位一定是0。

单组数据的时间复杂度为$O(31*n*10n)$。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct data
{
	int b , c , v;
	bool operator<(const data &a)const {return b < a.b;}
}a[110];
int f[32][1010];
int main()
{
	int n , m;
	while(~scanf("%d%d" , &n , &m) && (~n || ~m))
	{
		int i , j , p = 1 , now = 0;
		memset(a , 0 , sizeof(a)) , memset(f , 0 , sizeof(f));
		for(i = 1 ; i <= n ; i ++ )
		{
			scanf("%d%d" , &a[i].c , &a[i].v);
			while(!(a[i].c & 1)) a[i].c >>= 1 , a[i].b ++ ;
		}
		sort(a + 1 , a + n + 1);
		for(i = 0 ; i <= 30 ; i ++ )
		{
			while(p <= n && a[p].b == i)
			{
				now = min(now + a[p].c , m >> i);
				for(j = now ; j >= a[p].c ; j -- )
					f[i][j] = max(f[i][j] , f[i][j - a[p].c] + a[p].v);
				p ++ ;
			}
			for(j = 0 ; j <= now ; j ++ )
				f[i + 1][(j - (bool)(m & (1 << i)) + 1) >> 1] = max(f[i + 1][(j - (bool)(m & (1 << i)) + 1) >> 1] , f[i][j]);
			now = (now - (bool)(m & (1 << i)) + 1) >> 1;
		}
		printf("%d\n" , f[31][0]);
	}
	return 0;
}
时间: 2024-10-16 16:03:35

【bzoj1190】[HNOI2007]梦幻岛宝珠 分层背包dp的相关文章

luogu3188/bzoj1190 梦幻岛宝珠 (分层背包dp)

他都告诉你能拆了 那就拆呗.把每个重量拆成$a*2^b$的形式 然后对于每个不同的b,先分开做30个背包 再设f[i][j]表示b<=i的物品中 容量为$ j*2^i+W\&((1<<(i-1))-1) $(就是这一位是j+W的前i-1位)的最大权值(这个容量没必要填满) 然后f[i][j]就可以从f[i-1][j*2+W的第i-1位]转移过来,再拿着这个去更新本层的其他容量 最后答案就是f[x][1],x是W的最高位1的位数 1 #include<bits/stdc++.

【题解】 bzoj1190: [HNOI2007]梦幻岛宝珠 (动态规划)

bzoj1190,懒得复制,戳我戳我 Solution: 这道题其实是一个背包(分组背包),但是由于数字比较大,就要重新构造dp式子.啃了三天才懂. \(dp[i][j]\)表示背包容积为\(j*2^i\)时的最大价值. 首先,因为每一个物品一定是\(a*2^b\),我们可以按照\(b\)值先按照普通的分组背包去做,处理出每个\(b\)值所对应的\(dp\)值 然后我们就是要把这些\(dp\)值累积起来,选择每组最大显然不合适,因为有可能每个组都剩下空间,剩余空间累加起来的空间还可以放物品,我们

BZOJ 1190 HNOI2007 梦幻岛宝珠 动态规划

题目大意:01背包,其中weight<=2^30,但是每个weight都能写成a*2^b的形式,其中a<=10,b<=30 直接背包肯定TLE+MLE 考虑到每个weight都能写成a*2^b的形式,显然我们要按照b分层来进行背包 令f[i][j]表示有j*2^i+(w&(1<<i)-1)的空间时的最大价值 首先每层内部先做一个01背包 然后层与层之间再转移 从大到小枚举j 转移方程为f[i][j]=max{f[i][j],f[i][j-k]+f[i-1][min(k

bzoj1190--梦幻岛宝珠--分层dp+01背包(未完成)

Description 给你N颗宝石,每颗宝石都有重量和价值.要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大为,并输出最大的总价值.数据范围:N<=100;W<=2^30,并且保证每颗宝石的重量符合a*2^b(a<=10;b<=30) Input 输入文件中包含多组数据.每组数据的格式如下:第一行是两个正整数n和W,1≤n≤100,1≤W≤2^30,分别表示宝石的数目和最多能带走的宝石重量.接下来的n行,每行有两个正整数weighti和valuei,1≤weight

[BZOJ 1025] 游戏 置换群 背包DP

题意 对于一个 $n$ 阶置换群 $A$ , 它的循环节大小分别为 $a_1, a_2, ..., a_m$ , 则有 $\sum_{i = 1} ^ m a_i = n$ . 定义 $f(A)$ 为它的所有循环节的最小公倍数, 即 $f(A) = [a_1, a_2, ..., a_m]$ . 求在所有 $n$ 阶置换群中, $f(A)$ 有多少种取值. $n \le 1000$ . 分析 判断 $K$ 可不可取. $K = \prod_{i = 1} ^ r {s_r} ^ {t_r}$ 可

hdu 5234 Happy birthday 背包 dp

Happy birthday Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5234 Description 今天是Gorwin的生日.所以她的妈妈要实现她的一个愿望.Gorwin说她想吃很多蛋糕.所以他妈妈带她来到了蛋糕园. 这个园子被分成了n*m个方格子.在每一个格子里面,有一个蛋糕.第i行,第j列的格子中有一个重量为wij千克的蛋糕,Gorwin从左上角(1,1

hdu 1171 Big Event in HDU(背包DP)

题意: 杭电搬迁,有N种设备,每种设备有个价值V,数量M,要求将这些设备平分,使得平分后两边的总价值尽可能地相等. 输出两边各自的总价值. 思路: 背包DP后,P=所有的总价值/2,然后从P开始往两边找到第一个满足的价值. 可以降维,但是要注意for循环的顺序. 看代码. 代码: int v[55], m[55]; bool dp[250005]; int main(){ int n; while(scanf("%d",&n)!=EOF && n>=0){

POJ 1384 Piggy-Bank 背包DP

所谓的完全背包,就是说物品没有限制数量的. 怎么起个这么intimidating(吓人)的名字? 其实和一般01背包没多少区别,不过数量可以无穷大,那么就可以利用一个物品累加到总容量结尾就可以了. 本题要求装满的,故此增加个限制就可以了. #include <stdio.h> #include <stdlib.h> #include <string.h> inline int min(int a, int b) { return a < b? a : b; } c

BZOJ 1042 硬币购物(完全背包+DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1042 题意:给出四种面值的硬币c1,c2,c3,c4.n个询问.每次询问用d1.d2.d3.d4个相应的硬币能够拼出多少种总和为s? 思路:(1)首先,用完全背包求出f[i]表示四种硬币的数量无限制拼出i的方案数. (2)接着我们来理解 x=f[s]-f[s-(d1+1)*c1]的含义:x表示c1硬币的数量不超过d1个而其他三种硬币的数量不限制拼成s的方案数.我们举着例子来说明, 假设