BZOJ2064:分裂(状压DP)

本文介绍了一种算法,用于计算两个不同国家状态间通过合并与分裂达到彼此所需的最少操作次数。该算法首先验证两个集合的总和是否相等,接着采用特定策略确保找到最优解,最终实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

假设中国的国土总和是不变的。 每个国家都可以用他的国土面积代替, 又两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和。 一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积。 WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(分裂和合并各算一次操作),能让中国从当时状态到达现在的状态。

题解:
首先,两个集合能通过分裂合并得到,那他们的和一定相等,而且如果两个集合中没有任何一个子集和是相等的,只能通过将第一个集合合并完之后分裂成第二个集合,步数为 n1+n22 ,如果有子集的和相等,那么这就是个子问题,设最多可分成 k 个子集和,那么答案就为n1+n22k。问题就转化为使 k 最大。

不妨将第二个集合元素取负放入第一个集合,答案并不会影响。
对于一个集合,如果他的和不为0,那么肯定有一位数字是无用的,可以从更小的集合转移过来。
如果和为0,考虑依次删除每个元素(此时一定会枚举到一个最小的为0的集合,并且减去这个集合的贡献,也就是1),最后加上1就好了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int Maxn=(1<<20)+20;
int n1,n2,f[Maxn],sum[Maxn];

int main(){
    scanf("%d",&n1);
    for(int i=0;i<n1;i++)scanf("%d",&sum[(1<<i)]);
    scanf("%d",&n2);
    for(int i=0;i<n2;i++)scanf("%d",&sum[(1<<(n1+i))]),sum[(1<<(n1+i))]=-sum[(1<<(n1+i))];
    static int lim=(1<<(n1+n2))-1;
    for(int i=1;i<=lim;i++){
        sum[i]=sum[(i&(-i))]+sum[i-(i&(-i))];
        for(int j=0;(1ll<<j)<=i;++j){
            if(i&(1ll<<j))f[i]=max(f[i],f[i^(1ll<<j)]);
        }
        f[i]+=(!sum[i]);
    }
    printf("%d\n",n1+n2-2*f[lim]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值