当前位置: 首页 > news >正文

SOS-dp

\(\text{atcoder-arc100c}\)

SOS-dp 模板题。

首先讲一下 SOS-dp 是个什么东西,实际上就是优化子集枚举的一个算法,本质上是状压 dp。

这道题的弱化版是这样的:令 \(f_S = \sum\limits_{T \subseteq S} a_T\),求对于所有的 \(0 \le S < 2^n\)\(f_S\) 的值。

首先朴素算法的时间复杂度是 \(O(4^n)\) 的:

for(int S = 0; S < (1 << n); S ++)for(int T = 0; T < (1 << n); T ++) if((S & T) == T) f[S] += a[T];

朴素算法第二维枚举的时间复杂度太高了,我们只需要枚举 \(S\) 的子集。

如果 \(S\) 中有 \(k\)\(1\),那么它有 \(2^k\) 个子集,这样的 \(S\)\(\binom{n}{k}\) 个,总枚举次数为 \(\sum\limits_{k=0}^n \binom{n}{k}2^k=(1+2)^n=3^n\),用了二项式定理。所以时间复杂度就降到了 \(O(3^n)\),具体怎么不重不漏枚举子集可以看代码:

for(int S = 0; S < (1 << n); S ++) {f[S] = a[0];for(int T = S; T; T = (T - 1) & S) f[S] += a[T];
}

但这个时间复杂度还是太高了,于是我们就要用到 SOS-dp 了,即高位前缀和。

首先考虑三维的前缀和怎么写,我们这里不用基于容斥的前缀和计算方式,用逐维前缀和。

对于一般情形,给定 \(k\) 维数组 \(a\),大小为 \(n\),要求其前缀和 \(S\),有下式:

\[S_{i_1,\cdots,i_k} = \sum_{i_1' \le i_1} \cdots \sum_{i_k' \le i_k} a_{i_1',\cdots,i_k'} \]

从上式可以看出,\(k\) 维前缀和就等于 \(k\) 次求和。所以,一个显然的算法是,每次只考虑一个维度,固定所有其他维度,然后求若干个一维前缀和,这样对于 \(k\) 个维度分别求和之后,得到的就是 $k 维前缀和。

下面是一种三维前缀和的实现:

for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++)for(int k = 1; k <= n; k ++) s[i][j][k] += s[i][j][k - 1];
for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++)for(int k = 1; k <= n; k ++) s[i][j][k] += s[i][j - 1][k];
for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++)for(int k = 1; k <= n; k ++) s[i][j][k] += s[i - 1][j][k];

那么推广到这题的 \(n\) 维每一维只有 \(0/1\) 的前缀和,就很容易写出下面的伪代码:

// 初始化 f 数组
for(int i1 = 0; i1 <= 1; i1 ++) ··· for(int in = 0; in <= 1; in ++)if(in == 1) f[i1]···[in] += f[i1]···[in - 1];
···
for(int i1 = 0; i1 <= 1; i1 ++) ··· for(int in = 0; in <= 1; in ++)if(i1 == 1) f[i1]···[in] += f[i1 - 1]···[in];

因为每一维都是 \(0/1\),我们显然可以把他们全部二进制压缩到一维。那么就可以这样写了:

for(int S = 0; S < (1 << n); S ++) f[S] = a[S];
for(int i = 0; i < n; i ++) for(int S = 0; S < (1 << n); S ++)if(S >> i & 1) f[S] += f[S ^ (1 << i)];

注意,只有第 \(i\) 维为 \(1\) 才需要转移。于是这道弱化版就做完了。


下面我们来看原题,原题要求对于每个 \(1 \le k < 2^n\)\(\max\limits_{0 \le i < j < 2^n \land (i | j) \le k} a_i+a_j\),其中 \(|\) 表示按位或。

这个没法直接求,因为有两个变量且两个变量相互作用,需要稍微转化一下,这个转化感觉比较神仙。

\(A_k = \max\limits_{i \ne j \land i|j=k} a_i+a_j\),则 \(ans_k = \max\limits_{i=1}^k A_i\),由于答案是求 \(\max\),不妨扩展一下 \(A_i\) 的范围,把 \(i,j,k\) 看作二进制数,则让 \(A_k=\max\limits_{i \ne j \land i,j \subseteq k} a_i+a_j\),这样处理对答案没有影响。

于是我们成功的把两个变量拆开了,这个式子可以拆开成下式:

\[A_k = \max_{i \subseteq k} a_i + \max_{i \ne j,j \subseteq k} a_j \]

相当于求出满足 \(i \subseteq k\) 的最大值和次大值,这样就可以用 SOS-dp 做了。

pii merge(pii x, pii y) {if(y.fi > x.fi) swap(x, y);return {x.fi, max(x.se, y.fi)};
}int main() {ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n;for(int i = 0; i < (1 << n); i ++) cin >> a[i].fi, a[i].se = -INF;for(int i = 0; i < n; i ++) for(int S = 0; S < (1 << n); S ++)if(S >> i & 1) a[S] = merge(a[S], a[S ^ (1 << i)]);for(int S = 1; S < (1 << n); S ++) {res = max(res, a[S].fi + a[S].se);cout << res << "\n";}return 0;
}
http://www.jsqmd.com/news/810617/

相关文章:

  • FPGA新手避坑指南:Quartus II 13.1下NCO IP核的完整配置与授权实战
  • 2026江苏主任护师考试培训机构实力排名:3家培训机构全方位评测 - 医考机构品牌测评专家
  • 每日算法快闪赛
  • 百达翡丽鹦鹉螺出手前必看:广州五家门店实测谁更实在 - 奢侈品回收测评
  • 使用Helm Chart在Kubernetes部署高可用authentik身份认证中心
  • 089、Python玩转硬件:用pyserial搞定串口通信那些坑
  • 对比直接使用官方 API 观察到的 Taotoken 路由容错效果
  • 2026年丽水黄金回收哪家靠谱?五店深度测评与打分 - 生活测评君
  • GPU时代的有用浪费:从效率至上到算力换一切的工程范式转变
  • Java 21 面试常见问题汇总
  • 在持续集成环境中集成 Taotoken CLI 实现自动化配置与密钥轮换
  • 从0到1,构建你的第一个AI Agent:核心原理与实战指南
  • Cursor Pro破解终极指南:开源工具cursor-free-vip实现AI编程助手永久免费使用
  • 储能项目一操作记录
  • Zed编辑器深色光标主题:提升编码体验的视觉工程实践
  • 5.参考论文的文献引用没有标数字,要不要标数字?
  • 茉莉花插件:如何用Jasminum解决中文文献管理的三大痛点
  • 座机打电话时,能设置在对方屏幕上显示的公司名称吗?开通号码认证业务
  • 工程师如何从错误中学习:构建个人与团队的错误处理系统
  • 基于MCP协议的学术成果商业化AI管道:从论文到商业机会的自动化桥梁
  • 台湾产业转型:从代工制造到创新生态的挑战与机遇
  • 长期使用Taotoken聚合服务对项目API调用成功率的实际影响
  • 从技术段子到工程实践:构建无歧义的硬件开发沟通体系
  • 『订单税率+收货地址校验国家字段』功能上新|跨境运营更高效,Tigshop开源商城系统 JAVA v5.8.23 版本更新
  • 数字时代隐私保护:从法律困境到个人防御与产品设计
  • QML Color 颜色应用示例合集
  • 6.这个论文发表过吗?可以直接用吗?能过查重吗?
  • MySQL数据类型与约束 数值字符串日期
  • 大厂技术人的“隐形天花板”:为什么升到P8就上不去了?
  • 逻辑删除不等于物理销毁:KingbaseES 敏感数据擦除实战