思想
sosdp跟状压差不多,以5(101)为例,状压记录的是(100, 1)这两个状态,而sosdp记录的是(101, 100, 1, 0)这四个状态,也可以说是他的子集。即sosdp[mask]存的是 所有的a[i],其中i&mask == i或者i|mask == mask。
实现方法
//dp[j]记录子集的和
for(int i = 0; i < n; i ++){
for(int j = 0; j < (1 << n); j ++){
if((j >> i) & 1) dp[j] += dp[j ^ (1 << i)];
}
}
模拟过程
取二进制位数是3
001 | 010 | 011 | 100 | 101 | 110 | 111 | |
0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 |
1 | a1+dp[0]=a1+a0 | a2 | a3+dp[2]=a3+a2 | a4 | a5+dp[4]=a5+a4 | a6 | a7+dp[6]=a7+a6 |
2 | a1+a0 | a2+dp[0]=a2+a0 | a3+a2+dp[1]=a3+a2+a1+a0 | a4 | a5+a4 | a6+dp[4]=a6+a4 | a7+a6+dp[5]=a7+a6+a5+a4 |
3 | a1+a0 | a2+a0 | a3+a2+a1+a0 | a4+dp[0]=a4+a0 | a5+a4+dp[1]=a5+a4+a1+a0 | a6+a4+dp[2]=a6+a4+a2+a0 | a7+a6+a5+a4+dp[3]=a7+a6+a5+a4+a3+a2+a1+a0 |
例题
arc100_c
题意
给你一个n,然后是2^n个数下标从0开始,问你找到两个数下标是i, j,满足i | j <= k,使得这两个数的和最大。然后k分别取1到(2^n - 1),也就是说要输出2^n - 1 行最大值。
思路
i | j == k,说明i,j都是k的子集,那么要使得两个数和最大,只需要找到k的子集中最大值和次大值即可。然后i | j <= k,也就是说i | j == k - 1也可以,所以从左到右更新结果的最大值输出。
ac代码
#include<bits/stdc++.h>
using namespace std;
mt19937_64 rng(time(0));
#define io cin.tie(0);ios::sync_with_stdio(false);
#define ok(x, y) x >= 1 && x <= n && y >= 1 && y <= m
#define debug(x) cout<<#x<<"="<<x<<endl
#define lowbit(x) x&(-x)
#define pii pair<int,int>
#define mk make_pair
#define ll long long
#define ull unsigned long long
#define rs p<<1|1
#define ls p<<1
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 1e18;
inline ll read(){
ll p=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){p=(p<<1)+(p<<3)+(c^48),c=getchar();}
return f*p;
}
void print(__int128 x){
if(x<0) {putchar('-'); x=-x;}
if (x>9) print(x/10);
putchar('0'+x%10);
}
int a[1 << 18];
pii b[1 << 18];
pii cal(pii a, pii b){ //就是四个数放在大顶堆里面,然后取出前两个数返回,就是所要的最大值和次大值
priority_queue<int> q;
q.push(a.first);
q.push(a.second);
q.push(b.first);
q.push(b.second);
int t1 = q.top(); q.pop();
int t2 = q.top(); q.pop();
return {t1, t2};
}
void solve(){
int n; cin >> n;
for(int i = 0; i < (1 << n); i ++) {
cin >> a[i];
b[i] = {a[i], -inf}; //存放最大值和次大值,当前只有一个数,所以次大值定为-inf
}
for(int i = 0; i < n; i ++){
for(int j = 0; j < (1 << n); j ++){
if((j >> i) & 1) b[j] = cal(b[j], b[j ^ (1 << i)]); //b[j]取子集中最大值和次大值
}
}
int ans = -1;
for(int i = 1; i < (1 << n); i ++){
ans = max(ans, b[i].first + b[i].second); // 更新最大值,也就是1到i的最大值
cout << ans << endl;
}
}
int main(){
// freopen("1.in", "r", stdin);
// freopen("std.out", "w", stdout);
// cout << fixed << setprecision(6)
io;
int t = 1;
// cin >> t;
while(t --){
solve();
}
return 0;
}