【bzoj2064】【分裂】状态压缩表示合并子集

这篇博客介绍了如何运用状态压缩动态规划解决一个关于国家合并与分裂的数学模型问题。通过分析数据范围和操作上限,作者提出了当存在子集和相等时可以减少操作次数的思路,并详细阐述了利用动态规划找出「没用操作」最多的方案。文章最后给出了完整的代码实现。

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

这里写图片描述
(上不了p站我要死了,画师当然是wlop大大啦)

感觉这个做法还是挺难想的。
但还是总结一下思路吧。。

“只可意会不可言传的状压dp”(乱说)

Description
背景: 和久必分,分久必和。。。 题目描述: 中国历史上上分分和和次数非常多。。通读中国历史的WJMZBMR表示毫无压力。 同时经常搞OI的他把这个变成了一个数学模型。 假设中国的国土总和是不变的。 每个国家都可以用他的国土面积代替, 又两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和。 一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积。 WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(分裂和合并各算一次操作),能让中国从当时状态到达现在的状态。
Input
第一行一个数n1,表示当时的块数,接下来n1个数分别表示各块的面积。 第二行一个数n2,表示现在的块,接下来n2个数分别表示各块的面积。
Output
一行一个数表示最小次数。
Sample Input
1 6
3 1 2 3
Sample Output
2
数据范围:
对于100%的数据,n1,n2<=10,每个数<=50
对于30%的数据,n1,n2<=6,

当然,我们很自然的想到一种方法,就是先把所有的国家合并起来,再挨个拆开,这样的次数是n+m-2,也就是上限。

那么什么时候可以使操作次数减少呢?我们发现,当有一些子集的和相等时,直接将这些合并再拆开,就可以免去多余的合并操作。(然而我这一步愣是没想出来)

从反方面想,每当找到这样的子集,就相当于找到了一次没用的操作,就可以拿上限去减。但是,如果合并方式不同,可能会产生不同的“没用操作”,而我们希望找到一种“没用操作”最多的操作方案,此时就是dp了。
而每一个国家可以被选或不被选,在有两种状态的情况下,我们很容易想到状压,而数据范围也是可以的。

那么对于一个子集状态的01串f[s](这里我将原来以及现在的国家状态合并到了一个二进制数中),这样的集合一定是由其一个子集和一个元素(国家)合并过来,不同的合并方式会造成不同的“没用操作”。所以对于每个集合,枚举集合中的每个元素,就相当于枚举不同的合并操作,取max即可。
如果该状态s中原来国家和现在国家的面积之和相等,则该状态也是一个“没用操作”,答案要++

完整代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int f[1<<20],sum1[1<<10],sum2[1<<10];
int n,m,w[15],v[15];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    scanf("%d",&m);
    for(int i=1;i<=m;i++) scanf("%d",&v[i]);
    for(int i=1;i<(1<<n);i++){
        for(int j=i,k=1;j;j>>=1,k++) if(j&1) sum1[i]+=w[k];

    }
    for(int i=1;i<(1<<m);i++){
        for(int j=i,k=1;j;j>>=1,k++) if(j&1) sum2[i]-=v[k];
    }
    for(int i=1;i<(1<<(n+m));i++){
        for(int j=i,k=0;j;j>>=1,k++) if(j&1) f[i]=max(f[i],f[i^(1<<k)]);
        if(sum1[i&((1<<n)-1)]+sum2[i>>n]==0) f[i]++;
    }
    printf("%d\n",n+m-2*f[(1<<(n+m))-1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值