BZOJ2064 分裂

Address

Solution

  • 只可意会不可言传?不存在的。
  • 但感觉这题解法很神,代码也很神。
  • 可以知道操作次数的上界是 n+m2 n + m − 2 次( n1 n − 1 次合并成一块后再分裂 m1 m − 1 次)。
  • 考虑如果能把初始状态和目标状态分成和对应相等的两组,那么操作次数就变为 n+m2×2 n + m − 2 × 2 次。
  • 同样,如果能分为 k k 组,操作次数就变为 n+m2k 次。
  • 于是问题就转化为求 k k 的最大值。
  • 注意到 n1,n2 的范围很小,把初始状态和目标状态中的所有块压缩成一个 20 20 位的二进制串,来表示它们选取的状态。
  • 把目标状态中块的面积取反,则问题就被转化为选取尽量多面积和为 0 0 的子集。
  • 记状态 i 的面积和为 sum[i] s u m [ i ] ,则转移为 sum[i]=sum[lowbit(i)]+sum[ilowbit(i)] s u m [ i ] = s u m [ l o w b i t ( i ) ] + s u m [ i − l o w b i t ( i ) ] (其实任选两个 i i 的子集相加即可,这样写只是方便)。
  • 记状态 i 最多能选取的子集个数为 f[i] f [ i ] ,则转移为 f[i]=maxj=1n1+n2{f[i2j1]}+(sum[i]==0) f [ i ] = max j = 1 n 1 + n 2 { f [ i − 2 j − 1 ] } + ( s u m [ i ] == 0 ) (这里并不需要枚举子集转移,因为 i i 的子集肯定被 i2j1 的子集包含,之前已经转移过了)。
  • 时间复杂度 O(2n1+n2×(n1+n2)) O ( 2 n 1 + n 2 × ( n 1 + n 2 ) )

Code

#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
#include <cstdlib>

using namespace std;

inline int get()
{
    char ch; int res = 0; bool flag = false;
    while (ch = getchar(), !isdigit(ch) && ch != '-');
    (ch == '-' ? flag = true : res = ch ^ 48);
    while (ch = getchar(), isdigit(ch))
        res = res * 10 + ch - 48;
    return flag ? -res : res;
}

inline void CkMax(int &x, int y)
{
    if (x < y) x = y;
}

const int N = 11e5 + 5;
int n, m, sum[N], f[N];

int main()
{
    n = get();
    for (int i = 0; i < n; ++i) sum[1 << i] = get();
    m = get();
    for (int i = 0; i < m; ++i) sum[1 << n + i - 1] = -get();

    n += m;
    const int C = 1 << n;
    for (int i = 1; i < C; ++i)
    {
        int tmp = i & -i;
        sum[i] = sum[tmp] + sum[i ^ tmp];
        for (int j = 0; j < n; ++j)
            if (1 << j & i) CkMax(f[i], f[1 << j ^ i]);
        if (!sum[i]) ++f[i];
    }
    printf("%d\n", n - (f[C - 1] << 1));
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值