入门笔记+题集:SOSdp

1.介绍: 原文链接1.1.SOSdp(Sum over subsets dpSum \ over \ subsets\ dpSum over subsets dp)顾名思义,子集和dp。用来维护子集信息的。俗称高维前缀和.1.2.最常见的形式:求解dp(mask)=∑i∈maskaidp(mask)=\sum_{i \in mask}a_idp(mask)=∑i∈mask​ai​注意:i∈mask等价于i&mask=i等价于i∣ma
摘要由CSDN通过智能技术生成

2023大厂真题提交网址(含题解):

www.CodeFun2000.com(http://101.43.147.120/)

最近我们一直在将收集到的机试真题制作数据并搬运到自己的OJ上,供大家免费练习,体会真题难度。现在OJ已录入50+道2023年最新大厂真题,同时在不断的更新。同时,可以关注"塔子哥学算法"公众号获得每道题的题解。
在这里插入图片描述

1.介绍:

原文链接

1.1.SOSdp( S u m   o v e r   s u b s e t s   d p Sum \ over \ subsets\ dp Sum over subsets dp)顾名思义,子集和dp。用来维护子集信息的。俗称高维前缀和.

1.2.最常见的形式:求解 d p ( m a s k ) = ∑ i ∈ m a s k a i dp(mask)=\sum_{i \in mask}a_i dp(mask)=imaskai

注意: i ∈ m a s k 等价于 i & m a s k = i 等价于 i ∣ m a s k = m a s k i \in mask 等价于 i\&mask=i 等价于i|mask=mask imask等价于i&mask=i等价于imask=mask

常见的解法是 O ( 3 n ) O(3^n) O(3n)枚举子集。使用sosdp可以加速到 O ( n ∗ 2 n ) O(n*2^n) O(n2n).

1.3.做法: S ( m a s k , i ) S(mask,i) S(mask,i)代表集合 m a s k mask mask只有前 i i i位不同的子集 a i a_i ai和.
在这里插入图片描述
合法性:

无非就是证明计数不重不漏呗。对于任意一个 i i i的子集 j j j.它们一定有一段前缀是相同的。所以 i i i的子集的计数可以分解成 l o g i logi logi个阶段去做: i i i的全子集 = 前 l o g i logi logi个位不同的子集和=上面的转移。(这个就有点像前缀和优化了)

1.4.编码:

//iterative version
for(int mask = 0; mask < (1<<N); ++mask){
   
	dp[mask][-1] = A[mask];	//handle base case separately (leaf states)
	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];
}
//memory optimized, super easy to code.
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)];
}

解释:两种不同的dp顺序。第一种是正常的状压dp的顺序:从小集合往大集合dp。
第二种是从低位到高位去dp。

2.应用:

基础定义比较好理解,但是它的应用非常广泛,技巧多。需要刷比较多的题才能加深对 s o s d p sosdp sosdp的理解。

2.1.ARC100E

题目地址

题目大意:

给你 2 n 2^n 2n个数。问你每个 k ∈ [ 0 , 2 n − 1 ] k \in[0,2^n-1] k[0,2n1],求解 max ⁡ i   o r   j ≤ k { a i + a j } \max_{i\ or\ j\leq k}\{a_i+a_j\} maxi or jk{ ai+aj}.
n ≤ 18 n\leq18 n18

题目思路:
这个题需要比较奇妙的转换:

1. i ∣ j ≤ k i|j\leq k ijk比较难求解,将其变成 i ∣ j = g ≤ k i|j=g \leq k ij=gk,所以每个 i ∣ j = g i|j=g ij=g,求前缀最大值即可。

2.但是上面还是不好处理。我们直接转换成子集的形式: ( i ∣ j ) ∈ k (i | j) \in k (ij)k.那么也就是 i ∈ k & & j ∈ k i \in k \&\& j \in k ik&&jk.那么显然对每个 k k k,求其下标 i i i k k k的子集的最大值和次大值.最后前缀最大值即可.

代码:

pii a[maxn];
void trans (int i , int j)
{
   
    int mx = max (a[i].fi , a[j].fi);
    int se = max(min(a[i].fi , a[j].fi) , max (a[i].se , a[j].se));
    a[i].fi = mx;
    a[i].se = se;
}
int main()
{
   
    ios::sync_with_stdio(false);
    int n; cin >> n;
    int s = 1 << n;
    for (int i = 0 ; i < s ; i++){
   
        int x; cin >> x;
        a[i] = {
   x , -inf};
    }
    for (int j = 0 ; j < n ; j++){
   
        for (int i = 0 ; i < s ; i++){
   
            if (i >> j & 1) trans(i , i ^ (1 << j));
        }
    }
    int ans = -inf;
    for (int i = 1 ; i < s ; i++){
   
        ans = max(ans , a[i].fi + a[i].se);
        cout << ans << endl;
    }
    return 0;
}
2.2:CF1208

题目地址

题目大意:
给一个长度为 n n n的序列,求 m a x { a i ∣ ( a j & a k ) } , 1 ≤ i < j < k ≤ n max\{a_i|(a_j\&a_k)\},1 \leq i <j<k \leq n max{ ai(aj&ak)},1i<j<kn
n ≤ 1 e 6 n \leq 1e6

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值