首页 > 其他分享 >P3188 [HNOI2007]梦幻岛宝珠 题解

P3188 [HNOI2007]梦幻岛宝珠 题解

时间:2022-11-11 20:12:00浏览次数:62  
标签:10 梦幻岛 int 题解 P3188 times ch 背包 MAXN

一道不错的 dp 题,下令原背包容量为 \(m\)。

注意到 \(w_i=a\times 2^b\) 中 \(a,b\) 都比较小,尝试按照 \(a\) 或者 \(b\) 分组然后合并,但是显然如果我们按照 \(a\) 分组就会有个问题,那就是背包容量过大无法处理。

于是考虑按照 \(b\) 分组,将所有 \(b\) 相同的分在一组,每一组内做一次 01 背包,\(f_{i,j}\) 表示当前 \(b=i\),背包容量为 \(j\times 2^b,j\in [0,10\times n]\) 时的最大价值。

接下来就需要考虑如何合并背包,注意到既然我们按照 \(b\) 也就是二进制位分组了,那么背包的合并也肯定还是按照二进制分组,类似的设 \(g_{i,j}\) 表示当前选取了 \(b=0\sim i\) 这些背包,\(b=i\) 这一组一共选择的背包容量为 \(j\times 2^i\),剩下的 \(0\sim i-1\) 这些组的背包选择的容量和 \(m\) 二进制低 \(i-1\) 位相同。

则有转移方程:

\[g_{i,j}=\max\{f_{i,j-k}+g_{i-1,\min\{10\times n,2\times k+((m>>(i-1))\&1)\}}\} \]

解释就是考虑进位问题,从 \(i\) 位中选取 \(j-k\) 个,剩下的 \(k\) 个从 \(i-1\) 位进位而来,后面那一坨位运算就是考虑这一位进位完之后是 0 还是 1。

最后答案就是 \(g_{\lfloor\log_2m\rfloor,1}\)。

注意多测清空和开 long long。

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:P3188 [HNOI2007]梦幻岛宝珠
	Date:2022/11/11
========= Plozia =========
*/

#include <bits/stdc++.h>
typedef long long LL;
using std::vector;

const int MAXN = 100 + 5;
int n, m;
LL a[MAXN], b[MAXN], v[MAXN], f[35][MAXN * 10], g[35][MAXN * 10];
vector <int> p[MAXN];

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
	return sum * fh;
}
LL Max(LL fir, LL sec) { return (fir > sec) ? fir : sec; }

void Solve()
{
	for (int i = 0; i <= 30; ++i) p[i].clear();
	memset(f, 0, sizeof(f)); memset(g, 0, sizeof(g));
	for (int i = 1; i <= n; ++i)
	{
		int w = Read(); v[i] = Read();
		for (int j = 30; j >= 0; --j)
			if (w % (1 << j) == 0 && w / (1 << j) <= 10) { a[i] = w / (1 << j), b[i] = (1 << j); p[j].push_back(i); break ; }
	}
	for (int i = 0; i <= 30; ++i)
	{
		for (int j = 0; j < p[i].size(); ++j)
			for (int k = 10 * n; k >= a[p[i][j]]; --k)
				f[i][k] = Max(f[i][k], f[i][k - a[p[i][j]]] + v[p[i][j]]);
	}
	for (int i = 0; i <= 10 * n; ++i) g[0][i] = f[0][i];
	for (int i = 1; i <= 30; ++i)
	{
		for (int j = 0; j <= 10 * n; ++j)
			for (int k = 0; k <= j; ++k)
				g[i][j] = Max(g[i][j], f[i][j - k] + g[i - 1][std::min(2 * k + ((m >> (i - 1)) & 1), 10 * n)]);
	}
	printf("%lld\n", g[std::__lg(m)][1]);
}

int main()
{
	while (1) { n = Read(), m = Read(); if (n == -1) break ; Solve(); } return 0;
}

标签:10,梦幻岛,int,题解,P3188,times,ch,背包,MAXN
From: https://www.cnblogs.com/Plozia/p/16881614.html

相关文章

  • 汉诺塔问题及其变种问题解法(cpp版本)
    普通汉诺塔问题1.问题描述有三个柱子A、B、C,A柱子上有n个圆盘,圆盘的大小不等,大圆盘的在下,小圆盘的在上。将A柱子上的圆盘全部移动到C柱子上。每次只能移动一个圆盘,而且......
  • 【题解】P2260 [清华集训2012]模积和(数学,整除分块)
    【题解】P2260[清华集训2012]模积和比较简单的一道推式子的题。(清华集训居然会出这种水题的吗)题目链接P2260[清华集训2012]模积和题意概述求\[\sum_{i=1}^{n}\sum......
  • 【题解】CF1485C Floor and Mod(二分答案,整除分块)
    【题解】CF1485CFloorandModemmm……NOIP考前两周,跟CSP考前一样(虽然最后并没有去考),写篇题解增加以下RP(雾)。提供一篇思路大体和题解区相同但用了二分写法的题解。......
  • P2254 [NOI2005] 瑰丽华尔兹 题解
    注意到可以设朴素转移方程\(f_{t,i,j}\)表示\(t\)时刻钢琴在\((i,j)\)时的最长滑动距离,这样复杂度是\(O(nmt)\)的,过不去。不过听说加点奇怪的优化能过?考虑一段时......
  • MySQL慢查询(下):问题解决,应用总结
    上篇回顾继上两篇:​​MySQL慢查询(上):你知道为啥会慢么?​​​​MySQL慢查询(中):正确的处理姿势,你get到了吗?​​在以上两篇内容中,我们一起探索了这些内容:SQL执行过程查询SQL为什......
  • 「题解」Codeforces 1098D Eels
    暴力是,每次挑出最小的两个合并。需要观察到没有产生贡献的次数很小。考虑最小的那个数的大小,如果一次合并没有产生贡献,那么最小的数至少\(\times2\).所以最多会有\(\mat......
  • [题解] [CSP-J 2022] 逻辑表达式 思路整理
    标签:分治。题目传送门:P8815[CSP-J2022]逻辑表达式题目大意给一个包含0、1、|、&、(、)的逻辑表达式(保证正确)。在计算表达式时采用“短路”策略:计算表达式a&b......
  • vs 加入目录下的文件不在解决方案窗口显示(我的是unreal,其他的也成立),必须手动加的问
    1、网上说的显示全部然后把非活动的包含,我的可能是项目太大,不行;2、使用一个foldertosolutionfolder插件,也不行,这个会将子文件夹单独生成一个项目;最后方案:删除*.sln文件,......
  • 题解 P4778【Counting swaps】
    problem一次操作指随意选定\(x,y\)并交换\(a_x,a_y\),请问有多少种方案,能用最少的操作次数重排一个排列\(a\)?\(n\leq10^5,P=10^9+7\)。solution0连边\(i\toa_i\)......
  • 模拟与高精度题解
    此题目特征为储存数字超过longlong类型,c++无法用一个变量存储全部数字解法为开数组来储存各个位上的数字1.字符高精度直接以两种方式处理字符即可#include<bits/std......