Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • 线上赛板子(实时更新)
  • 数据结构

  • 数学

  • 计算几何

  • 动态规划

    • 背包问题
    • DP优化
    • SOSDP
      • 暴力方法
      • 子集
      • 超集
  • 图论

  • 字符串

  • 杂项

  • 算法笔记
  • 动态规划
martian148
2024-08-29
目录

SOSDP

# SOSDP

SOSdp 的全称为 Sum over Subsets dynamic programming 意思就是子集和dp,其实在我看来就是状压dp的一种

我们需要解决这样一个问题:

F[mask]=∑i∈maskA[i] F[mask]=\sum\limits_{i\in mask}A[i] F[mask]=i∈mask∑​A[i]

# 暴力方法

我们可以 O(n4)O(n^4)O(n4) 暴力枚举 maskmaskmask 和 iii 然后查看 iii 是不是 maskmaskmask 的子集

for(int mask =0;mask < (1<<N); ++i){
		for(int i = 0;i < (1<<N); ++i)
			if(i&mask == i)
				F[mask] += A[i];
	}
1
2
3
4
5

同理,可以 O(n3)O(n^3)O(n3) 枚举所有子集

for(int mask = 0;mask < (1<<N); ++mask){
		F[mask] = A[0];
		for(int i = mask; i > 0; i = (i-1)&mask)
			F[mask] += A[i];
	} 
1
2
3
4
5

# 子集

但是这样的效率都不高,我们定义 dp[mask][i]dp[mask][i]dp[mask][i] 代表 x&mask=x,x⊕mask<2i+1x\&mask=x,x\oplus mask <2^{i+1}x&mask=x,x⊕mask<2i+1 的 A[i]A[i]A[i] 的和,也就是从低到高的前 iii 为与 maskmaskmask 不同 A[x]A[x]A[x] 的和

image.png

从图中我们能看出转移方程

dp[mask][i]={dp[mask][i−1]mask&(2i)=0dp[mask][i−1]+dp[mask⊕(2i)][i−1]mask&(2i)=2i \text{dp}[\text{mask}][i] = \begin{cases} \text{dp}[\text{mask}][i - 1] & \text{mask} \& (2^i) = 0 \\ \text{dp}[\text{mask}][i - 1] + \text{dp}[\text{mask} \oplus (2^i)][i - 1] & \text{mask} \& (2^i) = 2^i \end{cases} dp[mask][i]={dp[mask][i−1]dp[mask][i−1]+dp[mask⊕(2i)][i−1]​mask&(2i)=0mask&(2i)=2i​

这里利用类似于 01 背包滚动的方法把一维滚掉了

for (int i = 0; i<(1<<N); ++i)
	F[i] = A[i];
for (int i = 0;i < N; ++i) 
	for (int mask = 0; mask < (1<<N); ++mask){
		if (mask & (1<<i)) F[mask] += F[mask ^ (1<<i)];
}
1
2
3
4
5
6

# 超集

SOSDP 还有另外一种姿势

如果我们需要求:

F[mask]=∑mask∈iA[i] F[mask] = \sum\limits_{mask\in i}A[i] F[mask]=mask∈i∑​A[i]

那么,我们可以对上面那棵树,从上到下 dfs,然后把每个节点的值加到它的子集上

但其实不用这么干,把 01 反转后,子集的关系也翻转了

本来以为自己发现了一个什么奇怪的东西,后面发现这个是超集,超集就是 01 反转后的子集

for (int i = 0;i < N; ++i) 
	for (int mask = 0; mask < (1<<N); ++mask){
		if (!(mask & (1<<i))) F[mask] += F[mask ^ (1<<i)];
}
1
2
3
4
上次更新: 2025/04/08, 18:03:31
DP优化
连通性相关

← DP优化 连通性相关→

最近更新
01
Java基础语法
05-26
02
开发环境配置
05-26
03
pink 老师 JavaScript 学习笔记
05-26
更多文章>
Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式