题目来源
Description
给你一个长度为 2 n 2^n 2n 的序列 a a a,对于每个 1 ≤ k < 2 n 1\le k<2^n 1≤k<2n,找出最大的 a i + a j a_i+a_j ai+aj,其中 i ∨ j ≤ k i\vee j\le k i∨j≤k 且 0 ≤ i < j < 2 n 0\le i<j<2^n 0≤i<j<2n。
- 1 ≤ n ≤ 18 , 1 ≤ a i ≤ 1 0 9 1\le n\le18,1\le a_i\le10^9 1≤n≤18,1≤ai≤109。
Solution
若把二进制数看为一个集合,则我们称二进制数 a ⊆ b a\subseteq b a⊆b,表示二进制数 b b b 的每一位 0 0 0 在 a a a 中均为 0 0 0(例如: 1010 1 2 ⊆ 1110 1 2 10101_2\subseteq11101_2 101012⊆111012,但 1010 1 2 ⊈ 1100 1 2 10101_2\not\subseteq11001_2 101012⊆110012)。
那么, i , j ⊆ k i,j\subseteq k i,j⊆k 的必要条件为 i ∨ j = k i\vee j=k i∨j=k,充分条件为 i ∨ j ≤ k i\vee j\le k i∨j≤k。
而由于我们要求的答案 a n s k = max k 0 = 1 k { max i ∨ j = k 0 , 0 ≤ i < j < 2 n { a i + a j } } ans_k=\max\limits_{k_0=1}^k\Big\{\max\limits_{i\vee j=k_0,0\le i<j<2^n}\{a_i+a_j\}\Big\} ansk=k0=1maxk{i∨j=k0,0≤i<j<2nmax{ai+aj}},则可以将其转化为求 max i , j ⊆ k , 0 ≤ i , j < 2 n { a i + a j } \max\limits_{i,j\subseteq k,0\le i,j<2^n}\{a_i+a_j\} i,j⊆k,0≤i,j<2nmax{ai+aj}。
则我们可以考虑运用 SOSDP,设 f k f_k fk 存储满足 i , j ⊆ k , 0 ≤ i , j < 2 n i,j\subseteq k,0\le i,j<2^n i,j⊆k,0≤i,j<2n 的 a i + a j a_i+a_j ai+aj 的最大值和次大值,则实际上可以将其看作为 n n n 维的 DP 进行了状态压缩,每一维只有 0 / 1 0/1 0/1 两种取值。
因此,我们应当先枚举维数 j j j,再将每一个满足第 j j j 位为 1 1 1 的 i i i 进行转移(即 i ∧ 2 j = 1 i\wedge2^j=1 i∧2j=1 时, f i xor 2 j → f i f_{i~\text{xor}~2^j}\to f_i fi xor 2j→fi)。
Code
#include <bits/stdc++.h>
using namespace std;
int n,a[(1<<18)+5];
pair<int,int> f[(1<<18)+5]; // 存储最大&次大值
pair<int,int> merge(pair<int,int> a,pair<int,int> b){
pair<int,int> res;
if (a.first>b.first) res.first=a.first,res.second=max(a.second,b.first);
else res.first=b.first,res.second=max(b.second,a.first);
return res;
}
int main(){
scanf("%d",&n);
for (int i=0;i<(1<<n);i++) scanf("%d",&a[i]),f[i]={a[i],0};
for (int j=0;j<n;j++) // 维数
for (int i=1;i<(1<<n);i++)
if ((i>>j)&1) f[i]=merge(f[i],f[i^(1<<j)]);
int ans=0;
for (int i=1;i<(1<<n);i++) ans=max(ans,f[i].first+f[i].second),printf("%d\n",ans);
return 0;
}