[J组模拟赛 #002 T4]分组选数

分组选数

题目大意:#

n 个数,第 i 个数是 ai,属于第 bi 个集合中。对于每个集合,若从中选出若干个数,则价值为这些数的异或和,总共的价值就是所有集合的价值的和。现在最多可以选 m 个数,问可以获得的最大价值是多少。

数据范围:#

对于 100% 的数据:

  • n,m2000
  • ai2000
  • bi2000

解题分析:#

朴素做法:#

假设 ai,j 为第 i 个集合中的第 j 个数。
首先,最质朴的想法就是设 fi,j,k,l 为第 i 个集合里,前 j 个数中选择 k 个数能否凑成 l。由于异或运算符是可逆的,所以转移方程就是:

fi,j,k,l=fi,j1,k,l|fi,j1,k1,l xor ai,j

然后再进行一个背包,这里 gi,j 为前 i 个集合中选 j 个数的最大答案:

gi,j=max(gi,j,gi1,jk+第 i 个集合里选 k 个数的答案)

PS:这里 k 是任意一个数

正解:#

但很明显,朴素做法时间复杂度是 O(n3),会超时,所以我们不妨观察一下状态。由于异或并不满足最优子结构,所以我们把目光转移到了这个:选择 k 个数
在尝试将状态变成 f[i][j][l] 表示第 i 个集合中,前 j 个数异或和为 l 最少要选择多少个数 后,我们发现这是可以计算的:

fi,j,l=min(fi,j1,l xor ai,j,fi,j1,l)

然后对于第 i 个集合中选择 k 个数的异或和,我们不妨把它设为 ci,k,然后很明显:

ci,f第 i 个集合的大小,j=max(ci,f第 i 个集合的大小,j,j)

注意:由于答案一定是最优的,所以只更新 最少要取多少个数 是没有问题的,因为大一点的更新后要用的数量更多反而会不好。
然后 g 的更新还是没有区别:

gi,j=max(gi,j,gi1,jk+ci,k)

然后这道题就做完了,时间复杂度 O(n2) (大概吧,反正是这个量级的)
AC Code:

# include <bits/stdc++.h>
using namespace std;

# define ll long long
# define lf double
# define int ll
# define GO(i,a,b) for(int i = a; i <= b; i ++)
# define RO(i,b,a) for(int i = b; i >= a; i --)
# define FO(i,u,head,e) for(int i = head[u]; i; i = e[i].next)
# define CI const int
# define pii pair<int,int>
# define F first
# define S second
# define PB(x) push_back(x)
# define mem(a,x) memset(a, x, sizeof a)

CI maxn = 2007;

int n, m;
map <int, vector <int> > p;
int x, y;
int f[3007][3007]; // 一个集合中前 i 个元素异或和为 j 所用的最少个数 
int s[3007]; // 用 i 个数最大的异或和 
int g[3007][3007]; // 前 i 个集合选 j 个数最大和 

signed main(){
	cin >> n >> m;
	GO (i, 1, n){
		scanf("%lld %lld", &x, &y);
		p[y].PB(x);
	}
	GO (i, 0, 3000)
		GO (j, 0, 3000)
			g[i][j] = -2e18;
	g[0][0] = 0;
	int ii = 0;
	int ans = -2e18;
	for (auto i : p){
		ii ++;
		auto x = (i.S);
		GO (j, 0, (int) (x.size()))
			GO (k, 0, 3000)
				f[j][k] = 2e18, s[k] = 0;
		f[0][0] = 0;
		GO (j, 1, (int) (x.size()))
			GO (k, 0, 2000)
				f[j][k] = min <int> (f[j - 1][k ^ x[j - 1]] + 1, f[j - 1][k]);
		GO (k, 0, 2000) if (f[(int)(x.size())][k] != 2e18) s[f[(int)(x.size())][k]] = max <int> (s[f[(int)(x.size())][k]], k);
		GO (j, 0, 2000)
			GO (k, 0, min <int> (j, (int) (x.size())))
				g[ii][j] = max <int> (g[ii][j], g[ii - 1][j - k] + s[k]);
	}
	GO (j, 0, m) ans = max <int> (ans, g[ii][j]);
	printf("%lld", ans);
	return 0;
}

作者:DE_aemmprty

出处:https://www.cnblogs.com/aemmprty/p/16799173.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   DE_aemmprty  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示