为了能到远方,脚下的每一步都不能少.|

Aurora-JC

园龄:3年1个月粉丝:3关注:4

2022-10-30 12:05阅读: 44评论: 0推荐: 0

BZOJ2064 分裂

Description

中国历史上上分分和和次数非常多。通读中国历史的WJMZBMR表示毫无压力。 同时经常搞OI的他把这个变成了一个数学模型。 假设中国的国土总和是不变的。 每个国家都可以用他的国土面积代替, 有两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和。 一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积。 WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(分裂和合并各算一次操作),能让中国从当时状态到达现在的状态。数据保证有解。

Input

第一行一个数 n1,表示当时的块数,接下来 n1 个数分别表示各块的面积。 第二行一个数 n2,表示现在的块,接下来 n2 个数分别表示各块的面积。

n1,n210

Output

一行一个数表示最小次数。

分析

状压dp好题。

如果说直接暴力的合并和分裂的话,最多需要的次数是 n+m2 次,其实可以这个基础上进行优化呢。例如 4,6,9,2 -> 10,5,6 完全可以把 4,6 单独处理 ,不用将他们再跟2,7 合并了,也不用额外的删除,答案也可以 2

于是发现如果上下可以被分成值相等的 k 块,那么总的次数是 n+m2k

然后现在就是要求 k 的最大值了。

用二进制表示某一个数选或不选。如果一个数是 n1,令它值为正,n2 的则值为负。

然后如果上面的一块和下面的一块值对应相等的话,就是所有的值加起来为 0

这样如果某一个子集的和为 0 ,就说明它可以互相变化
于是令 dpi表示选的状态为 i 时的最大的 k ,因为只有在 sumi=0 时,dpi++,其他状态都只能直接转移。

就可以使用状压dp了。

code

#include<bits/stdc++.h>
#define N 1<<22
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,n1,n2;
int sum[N],dp[N];
int main(){
n1=read();for(int i=1;i<=n1;++i) sum[1<<i-1]=read();
n2=read();for(int i=1;i<=n2;++i) sum[1<<(i+n1-1)]=-read();
n=1<<(n1+n2);
for(int i=1;i<n;++i){
int tmp=i&-i;sum[i]=sum[tmp]+sum[i-tmp];
for(int j=1;j<=n1+n2;++j){
if(i&(1<<j-1)) dp[i]=max(dp[i],dp[i^(1<<j-1)]);
}
if(!sum[i]) dp[i]++;
}
printf("%d\n",n1+n2-2*dp[n-1]);
return 0;
}

本文作者:南风未起

本文链接:https://www.cnblogs.com/jiangchen4122/p/16840928.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Aurora-JC  阅读(44)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起