SoS-DP 学习笔记

reference: https://codeforces.com/blog/entry/45223

前置技能: 状态压缩 DP

SoS-DP 全称是 Sum over Subsets Dynamic Programming,即子集和 DP,属于状态压缩 DP 中的一个经典问题的优化。

1. 问题引入

f [ s ] = ∑ i ∈ s a [ i ] f[s]=\sum\limits_{i\in s} a[i] f[s]=isa[i] f [ i ] = ∑ i ∈ s a [ s ] f[i]=\sum\limits_{i\in s} a[s] f[i]=isa[s],我们拿出第一个问题来讨论。

2. 朴素写法

O ( 4 n ) \mathcal{O}(4^n) O(4n) 暴力枚举:

int up = 1 << n;
for (int s = 0; s < up; s++) {
	for (int i = 0; i < up; i++) {
		if (s & i == i) f[s] += a[i];
	}
}

3. 稍微优化

O ( 3 n ) \mathcal{O}(3^n) O(3n) 枚举子集:

for (int s = 0; s < up; s++) {
	f[s] = a[0];
	for (int i = s; i > 0; i = (i-1)&s) f[s] += f[i];
}

4. 使劲优化

f [ s ] [ i ] f[s][i] f[s][i] 表示 x & s = x x\&s=x x&s=x x ⊕ s < 2 i + 1 x\oplus s<2^{i+1} xs<2i+1 a [ x ] a[x] a[x] 的和,有 f [ s ] [ i ] = f [ s ] [ i − 1 ] + f [ s ⊕ 2 i ] [ i − 1 ] f[s][i]=f[s][i-1]+f[s\oplus2^i][i-1] f[s][i]=f[s][i1]+f[s2i][i1],转移图如下:

SoS-DP 学习笔记_第1张图片

那么我们就有了下面的写法:

// iterative version
for (int s = 0; s < up; s++) {
	dp[s][-1] = a[s];
	for (int i = 0; i < n; i++) {
		if (s & (1 << i)) dp[s][i] = dp[s][i-1] + dp[s^(1<

可以证明这个时间复杂度是 O ( n ⋅ 2 n ) \mathcal{O}(n\cdot2^n) O(n2n) 非常优秀。

5. 例题

Codeforces - 449D

Codeforces - 1028F

你可能感兴趣的:(动态规划(DP),技术活,Codeforces)