BZOJ2064 分裂
题意
背景: 和久必分,分久必和。。。 题目描述: 中国历史上上分分和和次数非常多。。通读中国历史的WJMZBMR表示毫无压力。 同时经常搞OI的他把这个变成了一个数学模型。 假设中国的国土总和是不变的。 每个国家都可以用他的国土面积代替, 又两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和。 一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积。 WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(分裂和合并各算一次操作),能让中国从当时状态到达现在的状态。
\(n_1,n_2 \leq 10\)
Sakits的题解
显然最多的次数就是将初始集合全部合成一个数然后再分成目标集合,也就是次数上界为\(n+m-2\)
如果我们可以找到初始集合的某个子集的元素和与目标集合的某个子集的元素和相等,那么我们可以少合一次,少分一次,也就是说我们每多找到一个元素和相等的子集我们的次数就可以-2。换句话说把初始集合和目标集合分成尽量多的子集让这些子集都能对应(子集元素和相等),如果能分成\(x\)个子集那么次数就是\(n+m-2x\)
所以问题转化成怎么分最多子集能相对应。\(f(s_1,s_2)\)为把\(s_1\)和\(s_2\)最多能分成几个能相对应的子集(子集元素和相等)。
当\(s_1\)的元素和!=\(s_2\)的元素和,就没法用上所有元素分成相对应的子集,那我们就枚举\(s_1\)中的元素\(i\)或\(s_2\)中的一个元素\(j\),就有\(f(s_1,s_2)=\max\{f(s_1\ \textrm{xor}\ i,s_2),f(s_1,s_2\ \textrm{xor}\ j)\}\)
当\(s_1\)的元素和==\(s_2\)的元素和,我们可以用上全部元素,在这时候我们要是去掉其中一个元素,肯定会少一个子集,于是我们还是枚举\(s_1\)中的元素\(i\)或\(s_2\)中的一个元素\(j\),就有\(f(s_1,s_2)=\max\{f(s_1\ \textrm{xor}\ i,s_2),f(s_1,s_2\ \textrm{xor}\ j)\}+1\)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
rg T data=0;
rg int w=1;
rg char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return data*w;
}
template<class T>T read(T&x)
{
return x=read<T>();
}
using namespace std;
typedef long long ll;
co int N=10;
int n,m;
int sn[1<<N],sm[1<<N];
int f[1<<N][1<<N];
int lowbit(int x)
{
return x&-x;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n);
for(int i=0;i<n;++i)
read(sn[1<<i]);
read(m);
for(int i=0;i<m;++i)
read(sm[1<<i]);
for(int i=1;i<(1<<n);++i)
sn[i]=sn[i^lowbit(i)]+sn[lowbit(i)];
for(int i=1;i<(1<<m);++i)
sm[i]=sm[i^lowbit(i)]+sm[lowbit(i)];
for(int i=1;i<(1<<n);++i)
for(int j=1;j<(1<<m);++j)
{
for(int k=0;k<max(n,m);++k)
{
if(i&(1<<k))
f[i][j]=max(f[i^(1<<k)][j],f[i][j]);
if(j&(1<<k))
f[i][j]=max(f[i][j^(1<<k)],f[i][j]);
}
if(sn[i]==sm[j])
f[i][j]+=1;
}
printf("%d\n",n+m-2*f[(1<<n)-1][(1<<m)-1]);
return 0;
}