[BZOJ 2064]分裂
[BZOJ 2064]分裂
题目
背景: 和久必分,分久必和。。。 题目描述: 中国历史上上分分和和次数非常多。。通读中国历史的WJMZBMR表示毫无压力。 同时经常搞OI的他把这个变成了一个数学模型。 假设中国的国土总和是不变的。 每个国家都可以用他的国土面积代替, 又两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和。 一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积。 WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(分裂和合并各算一次操作),能让中国从当时状态到达现在的状态。
INPUT
第一行一个数n1,表示当时的块数,接下来n1个数分别表示各块的面积。 第二行一个数n2,表示现在的块,接下来n2个数分别表示各块的面积。
OUTPUT
一行一个数表示最小次数。
SAMPLE
INPUT
1 6
3 1 2 3
OUTPUT
2
解题报告
这状压有毒= =
我们考虑,在最坏的情况下,肯定就是把所有的合起来,再分裂成终态,这样的答案是$n+m-2$
然而显然,当我们可以从第一个数列中选出一些数的和与第二个数列中的某些数的和相等时,我们就没有必要完全合并它们再分裂,次数就减少了$2$
设$f[i]$表示状态为$i$的有多少组满足以上条件,则总方案数为$n+m-2-2\times f[(1<<(n+m))-1]$,但是显然会多算一个$0$的情况,所以最终答案为$n+m-2\times f[(1<<(n+m))-1]$
接下来就是如何转移了,我们令以前的和为正,现在的为负,则和为$0$就可以凑成一组满足条件的
所以我们引入数组$sum$,$sum[i]$表示状态$i$的面积之和,则我们可以递推求解,$sum[i]=sum[lowbit(i)]+sum[i-lowbit(i)]$
然后直接转移就行了
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 using namespace std; 6 inline int read(){ 7 int sum(0); 8 char ch(getchar()); 9 for(;ch<'0'||ch>'9';ch=getchar()); 10 for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar()); 11 return sum; 12 } 13 int n1,n2,n,st; 14 int sum[1<<21],f[1<<21]; 15 inline int lowbit(int x){ 16 return x&-x; 17 } 18 int main(){ 19 n1=read(); 20 for(int i=1;i<=n1;++i) 21 sum[1<<(i-1)]=read(); 22 n2=read(); 23 for(int i=n1+1;i<=n1+n2;++i) 24 sum[1<<(i-1)]=-read(); 25 n=n1+n2,st=(1<<n)-1; 26 for(int i=1;i<=st;++i){ 27 sum[i]=sum[lowbit(i)]+sum[i^lowbit(i)]; 28 for(int j=1;j<=n;++j) 29 if(i&(1<<(j-1))){ 30 int tmp(i-(1<<(j-1))); 31 f[i]=max(f[i],f[tmp]); 32 } 33 if(!sum[i]) 34 ++f[i]; 35 } 36 printf("%d",n-(f[st]<<1)); 37 }