CF_从多个数组中各选择1个元素,且总和最大

这是一篇关于解决算法问题的博客,内容涉及从多个不等长数组中选择元素以最大化总和,同时考虑了特定方案的禁止选择条件。文章讨论了错误的线性扫描思路,并提出了一种优化后的解决方案,利用哈希二分法在限制条件下找到最大和的方案。
摘要由CSDN通过智能技术生成

添加链接描述

有n[1, 10]个 不等长的数组长度2e5,且每个数组,都是sort
[1, 10]
[2, 3, 10]
[4, 5, 6, 10]
你需要从这n个数组中,选择1个元素,即一共选n个元素(且该选择方案,n个元素和是最大的

显然,每个数组选择最后元素,这种方案是最大的。

有m[1, 1e5]ban方案(每个方案长度是n 表示对这n个数组的选择情况),即这些方案,是不能选择的。

从上图来看,最优方案依次是: 10,10,10 10,10,6

如果我们ban掉:[2,3,4]。 则最大方案是:[2,3,3] = 10,10,6

最终,输出最大的方案(如果有多个,则任意即可。) 答案保证有解


先看个错误的思路:

令这n个数组的长度,依次是:n1, n2, n3, ...
令最初i = n1, j = n2, z = n3
FOR(st, 0, (1<<n)-1, 1),如果二进制是1,则往左退一位。

如果,所有st都是ban的,则让i--, j--, z--
即:如果i,j,z i-1,j,z i,j-1,z i,j,z-1 i-1,j-1,z i-1,j,z-1 i,j-1,z-1 i-1,j-1,z-1 都是baned。
则让i--, j--, z--,说明: 答案里,一定没有n1, n2, n3

很容易会这么想。
其实不然。

[1, 1, 100]
[1, 1, 100]
[1, 1, 100]
比如,ban掉所有的方案,只剩下:3, 3, 1。 即,最终答案是100, 100, 1


即,这其实是很复杂的。 不是线性扫描就能做到的。


唉,CF的题,思维跳跃性 都比较高…

由于肯定有解,比如答案是i, j, k
(如果:i+1, j, k 没有ban,则: i+1,j,k是优于i,j,k的!)
(如果:i, j+1, k 没有ban,则: i,j+1,k是优于i,j,k的!)
(如果:i+1, j+1, k 没有ban,则:i+1, j+1, k是优于i,j,k的!)

我们用st = [1, (1<<n)-1],来表示,上面的(+1)操作。

则,对于所有的ans + st 这个方案

  • 要么不存在。(比如,ans中 某个已经在最右侧) + (st)后,就溢出了
    这种方案属于非法的。
  • 要么,该方案,一定是ban的!!! (因为,该方案 优于 ans)

假如说:

  • 所有的ans + st 方案,都是非法的。 说明: ans全部都在最右侧!!!
    这种情况,我们特判掉。

否则,所有的ans + st方案里,至少存在1个方案, 他是合法 没有溢出的。
而且,这个方案,是ban的!!!

那么此时,就可以反向: 根据所有ban,然后 - st后,获得一个方案(如果该方案 是没有ban的。则该方案 一定会找到ans
会用到: 判断一个方案,是否在ban里。(哈希二分)

时间:(枚举所有ban1e5) * (st=1024)* (二分20 * k超时 (k是:哈希每次判断大小的时间,即vector的长度)


这里要优化。

其实,这里的st, 不需要是2^n! 只需要n (1, 2, 4, 8, 16..)即可。

比如我们看,有>=2个1的st

比如一个ban的方案:3, 3, 2, 他通过st = 3找到了 ans2, 2, 2
又因为, ans的所有的st都是ban的。

即此时有:3, 2, 2,一定是ban的!!

故,通过 ban的方案3,2,2,配合st = 1,也可以找到ans!!!

因此,st 从原来的2^n,变成n!!


由于每次需要判断,一个方案 是否在 ban里。 (即,暂时先用 简单的 哈希二分)

代码是:

比如有n个数组,每个数组长度是`n1, n2, n3, ...`

VE< VE<int> > ban( m); ' 共m个ban,每个ban长度是n。这是题目录入的 '

sort( ban.begin(), ban.end()); ' 哈希二分 '

{ ' 特判 '
	if( binary_search( ban, {n1,n2,n3,...}) == false){
		答案是: (n1, n2, n3...);
		return;
	}
}

for( const auto & _ban : ban){
	auto _t = _ban;	
' 不可以直接用_ban,因为对_ban进行修改,意味着 修改了ban,而ban是不能修改的!!'
' 因此,在for循环时,写上const,是最好的!!! '
	for( auto & i : _t){
		if( i != 1){
			-- i; ' 此时的_t,一定会遇到 ans'
			
			if( binary_search( ban, _t) == false){
				获取_t对应的总和,尝试更新ans。
			}
		
			++ i;
		}
	}
}

总时间是: m * n * log(m) * k(ve的长度) = 1e5 * 10 * 20 * n


思路非常新颖!!!

反过来, 根据ban,去(枚举)ans。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值