「学习笔记」SOS DP

本文详细介绍了SOS DP(Sum over Subsets dynamic programming)算法,包括其概念、例题解析和解决方案。通过枚举子集和优化的SOS DP方法,解决了涉及子集和计算的问题。此外,文章还探讨了SOS DP在求解超集和的应用,并推荐了相关习题进行实践。
摘要由CSDN通过智能技术生成

SOS DP 学习笔记

0.0 前言

本文大部分译自 CF 博客上的原文。Link here

0.1 前置知识

  • 状压 DP

1.0 简介

SOS DP,全称 Sum over Subsets dynamic programming,意为子集和 DP,用来解决一些涉及子集和计算的问题。

1.1 例题引入

给定一个含 2 N 2^N 2N 个整数的集合 A,我们需要计算:对于每个集合 x ∈ A x\in A xA,求 x x x 中所有元素 i i i A [ i ] A[i] A[i] 的和,即求:

F [ s t a ] = ∑ i ⊆ s t a A [ i ] {F[sta]=\sum\limits_{i\subseteq sta} A[i]} F[sta]=istaA[i]

1.2 解决方案

1.2.1 朴素算法

直接按照题意模拟即可,复杂度为 O ( 4 N ) ​ O(4^N)​ O(4N)

for(int sta=0;sta<(1<<N);sta++)
  	for(int i=0;i<(1<<N);i++)
      	if((sta&i)==i)F[sta]+=A[i];

1.2.2 枚举子集

对于每个状态,我们只遍历它的子集而去除了无关状态。如果一个状态的二进制位上只有 k k k 1 1 1,我们只需枚举它的 2 k 2^k 2k 个子集。这样的状态一共有 ( N k ) \dbinom{N}{k} (kN) 个,因此总迭代次数 = ∑ k = 0 N ( N k ) 2 k = ( 1 + 2 ) N = 3 N =\sum\limits_{k=0}^N{\dbinom{N}{k}2^k}=(1+2)^N=3^N =k=0N(kN)2k=(1+2)N=3N,时间复杂度即为 O ( 3 N ) O(3^N) O(3N)

for(int sta=0;sta<(1<<N);sta++)
{
   
    F[sta]=A[0];
	for(int i=sta;i>0;i=(i-1)&sta)
		F[sta]+=A[i];
}

1.2.3 SOS DP

上面枚举子集的方法有明显的缺陷:当一个状态的二进制位上有 k k k 0 0 0 时,它将在其他(不包含本身)状态迭代时被访问 2 k − 1 2^k-1 2k1 次,存在重复的计算。

而产生这种现象的原因就是:我们没有在 A [ x ] A[x] A[x] 被不同 F [ s t a ] F[sta] F[sta] 利用时建立一定的联系。我们应添加另一个状态来避免上述的重复计算。

定义状态 S ( s t a ) = { x ∣ x ⊆ s t a } S(sta)=\{x|x\subseteq sta\} S(sta)={ xxsta}。现在我们把这个集合划分为不相交的组。

S ( s t a , i ) = { x ∣ x ⊆ s t a & & s t a ⊕ x < 2 i + 1 } S(sta,i)=\{x|x\subseteq sta \&\& sta\oplus x<2^{i+1}\} S(sta,i)={ xxsta&&stax<2i+1}。我们将二进制位数从 0 0 0 开始从低位向高位表示,那集合 S ( s t a , i ) S(sta,i) S(sta,i

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值