关于子集问题的一种解法

关于子集问题的一种解法

1.引入

给定 n 以及两个长度为 2n 的序列 ab ,对于每个 k
mini|j=k{ai+bj}

对于这类不可逆的运算,常规的 FWT 无法解决,当然也可能是我太菜

而暴力枚举的复杂度为 22n 无法接受,

这时候我们就需要用到分治乘法。

2.记号

以下记号借用了集合幂级数的一些内容。

考虑将长度为 2n 序列 a 中下标二进制最高位为 1 的部分记录为 a+ ,为 0 的部分记为 a ,记录答案序列为 f

首先,显然 f=f++f

并且,我们有

f+=min{min(a++b),min(a+b+),min(a++b+)}

f=a+b

注意到这时我们已经把问题分成四个长度为 2n1 的子问题,但是如果暴力分治求解复杂度还是 22n 并且还多了一堆常数

不过第一个式子可以做如下化简,

f+=min(min(a+,a)+b+,min(a+b+))

min(a+,a) 是可以在分治过程中处理的,

这是我们的分治过程为 T(2n)=3T(2n1)+O(2n)

由主定理知,该算法的复杂度为 Θ(3n)

如果采用合适的实现,空间复杂度可以做到 O(2n)

3.程序实现

该方法实现实际上起来异常的简单。

int a[maxN], b[maxN], c[maxN];
int dat[maxN];
void multi(int *a, int *b, int *c, int n, int *dat) {
  // a,b为上文所定义,c为答案数组,dat为临时数组,用来保存a的内容,n为长度
  // 由于min运算的不可逆性质,必须开个数组记录原内容
	n >>= 1;
	if (!n) { c[0] = min(a[0] + b[0], c[0]); return; }
	multi(a, b, c, n, dat); 
	multi(a + n, b, c + n, n, dat);
	for (int i = 0; i < n; ++i) {
		dat[i] = a[i];
	  a[i] = min(a[i], a[i + n]);
	}
	multi(a, b + n, c + n, n, dat + n);
	for (int i = 0; i < n; ++i)
		a[i] = dat[i];
}

4.习题

FWT能做的它都能做。

貌似还没什么习题。

也许可以用 AT4168 做板子?

posted @   LitDarkness  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示