【强基】SOS DP(子集 DP)

Part 1:前置知识

1、状压 DP

2、基本的位运算操作

Part 2:SOS DP

(以下的内容大部分翻译至CF上的原文

1、例题引入

给定一个含 2 N 2^N 2N 个整数的集合 A A A,我们需要计算: ∀ x ⊆ A \forall x \subseteq A xA x x x 中所有元素 i i i A [ i ] A[i] A[i] 的和,即求:
F [ m a s k ] = ∑ i ⊆ m a s k A [ i ] F[mask]=\sum\limits_{i \subseteq mask}^{}{A[i}] F[mask]=imaskA[i]

2、解题思路

法一:暴力枚举
  • 我们可以枚举每一个 m a s k mask mask,再枚举集合中的所有元素 i i i,判断 i i i 是属于集合 m a s k mask mask。这样做的时间复杂度为 O ( 4 N ) O(4^N) O(4N)

  • 代码

for(int mask=0; mask<(1<<N); mask++)
  	for(int i=0; i<(1<<N); i++)
		if((mask&i)==i)
		  	F[mask]+=A[i];
法二:枚举子集
  • 对于任意 m a s k mask mask,如果它做的二进制位上有 k k k 1 1 1,那么它就有 2 k 2^k 2k 个子集,我们只需遍历这些子集便可。
    时间复杂度为 O ( ∑ k = 0 N ( N k ) 2 k ) = O ( ( 1 + 2 ) N ) = O ( 3 N ) O(\sum\limits_{k=0}^{N}{\tbinom{N}{k}2^k})=O((1+2)^N)=O(3^N) O(k=0N(kN)2k)=O((1+2)N)=O(3N)。(可由二项式定理证明)

  • 代码

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];
}
法三:SOS DP
  • 在我们之前的方法中存在明显的缺陷:当一个状态的二进制位上有 k k k 0 0 0 时,它将被访问 2 k 2^k 2k 次,存在重复的计算。

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

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

    S ( m a s k , i ) = { x ∣ x ⊆ m a s k ,   m a s k ⊕ x < 2 i + 1 } S(mask,i)=\{x|x\subseteq mask,\:mask⊕x<2^i+1\} S(mask,i)={xxmask,maskx<2i+1},表示只有第 i i i 位以及更低位与 m a s k mask mask 不同的 x x x 的集合。

    举个例子: S ( S( S(101 1010 , 3 ) = { 1010,3)=\{ 1010,3)={101 1010 , 1010, 1010,101 0010 , 0010, 0010,101 1000 , 1000, 1000,101 0000 } 0000\} 0000} 。其中 1010 1010 1010 即为 m a s k mask mask 的第 3 3 3 位至第 0 0 0 位,集合中的元素的加粗部分应与 m a s k mask mask 保持一致。

  • 现在让我们尝试将 m a s k mask mask x x x 建立联系,下面分两种情况讨论:

    1. m a s k mask mask 的第 i i i 位是 0 0 0
      不难发现, m a s k mask mask x x x 的第 i i i 位均为 0 0 0。因此 x x x 仅有第 0 0 0 i − 1 i-1 i1 位与 m a s k mask mask 不同,故有 S ( m a s k , i ) = S ( m a s k , i − 1 ) S(mask,i)=S(mask,i-1) S(mask,i)=S(mask,i1)

    2. m a s k mask mask 的第 i i i 位是 1 1 1
      那么 S ( m a s k , i ) S(mask,i) S(mask,i) 就由两部分组成:
      ( 1 )   (1)\: (1) x x x 的第 i i i 位为 0 0 0,即为 S ( m a s k ⊕ 2 i , i − 1 ) S(mask⊕2^i,i-1) S(mask2i,i1)
      ( 2 )   (2)\: (2) x x x 的第 i i i 位为 1 1 1,即为 S ( m a s k , i − 1 ) S(mask,i-1) S(mask,i1)

  • 综上,可以得出结论
    S ( m a s k , i ) = { S ( m a s k , i − 1 ) i t h   b i t   0 S ( m a s k , i − 1 ) + S ( m a s k ⊕ 2 i , i − 1 ) i t h   b i t   1 S(mask,i)=\begin{cases}S(mask,i-1)&&i^{th}\:bit \:0\\S(mask,i-1)+S(mask⊕2^i,i-1)&&i^{th}\:bit\:1\end{cases} S(mask,i)={S(mask,i1)S(mask,i1)+S(mask2i,i1)ithbit0ithbit1

    不难计算,这种做法的时间复杂度为 O ( N 2 N ) O(N2^N) O(N2N)

  • 代码(二维版)

for(int mask=0; mask<(1<<N); mask++)
{
	dp[mask][-1] = A[mask];
	
	for(int i=0; i<N; i++)
	{
		if(mask&(1<<i))
			dp[mask][i]=dp[mask][i-1]+dp[mask^(1<<i)][i-1];
		else
			dp[mask][i]=dp[mask][i-1];
	}
	
	F[mask]=dp[mask][N-1];
}
  • 代码(一维版)
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)];
	}
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
强基计划数学备考十五讲pdf》是一本数学备考资料,对于参加强基计划的学生来说,具有重要的参考价值。这本资料包括了十五个章节,每个章节都涵盖了数学备考的关键知识点。 这本资料的第一章从基础的数学概念开始,介绍了数的性质、整数、有理数等基本概念。随后的几章介绍了代数、几何、概率、统计等不同领域的数学知识。每个章节都提供了大量的例题和习题,帮助学生巩固知识点。 这本资料的特点之一是内容丰富全面。它不仅涵盖了高中数学的基础知识,还包括了一些高阶的数学概念和解题技巧,可以满足不同层次的学生需求。此外,这本资料还给出了详细的解题思路和方法,有助于学生理解和掌握解题的逻辑和技巧。 另一个亮点是这本资料的实用性。它根据强基计划的考纲和要求编写,能够帮助学生有针对性地备考,提高他们的数学成绩。同时,每个章节的习题数量也很充足,让学生在复习中得到充分的练习和巩固,提高他们的解题能力。 总的来说,《强基计划数学备考十五讲pdf》是一本对于参加强基计划的学生来说非常实用的备考资料。它全面、详细地介绍了数学备考的关键知识点,帮助学生提高解题能力,提高数学成绩。对于想要在强基计划中取得好成绩的学生来说,这本资料是不可或缺的学习工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值