bzoj2064分裂

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,

看数据范围,显然状压dp...
设状态i对应的面积和为sum[i],则当sum[i]==sum[j]时i才可以变成j
所以我们得到了一个很显然的状态转移方程.记f[S1][S2]为使用初始集合的S1子集得到目标集合的S2子集的最小花费,则f[s1|s3][s2|s4]=f[s1][s2]+f[s3][s4].
如果暴力枚举每个子集,O((2^n)^4),显然会T.
假如我们将初始集合分成了x个子集,那么最后的花费就是初始集合元素个数(A)+目标集合元素个数(B)-2*x  { (B-x)+(A-x) }
设f[i][j]为i集合和j集合中最多有的sum相等的集合数,则
f[i][j]=min(f[i][l],f[k][j])(i,j状态代表的sum值不相等)
f[i][j]=min(f[i][l],f[k][j])+1(i,j状态代表的sum值相等)
答案即为n1+n2-(f[S][T]<<1);
代码
#include <cstdio>
#include <iostream>
#include <cstring>
#define maxn 15
#define edge (1<<10)+10
using namespace std;
inline void read(int& x)
{	char c=getchar();x=0;int y=1;
	while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	x*=y;
}
template<typename T>inline void m_max(T& x,T y){if(x<y) x=y;}
int n1,n2,edg1,edg2,A[4][maxn],sum[4][edge],f[edge][edge];
inline int getsum(int x,int o)
{	int tmp=0;
	for(int i=1;x;x>>=1,++i) if(x&1) tmp+=A[o][i];
	return tmp;
}
void init()
{	for(int i=1;i<=edg1;++i) sum[1][i]=getsum(i,1);
	for(int i=1;i<=edg2;++i) sum[2][i]=getsum(i,2);
}
void dp()
{	for(int i=1;i<=edg1;++i)
		for(int j=1;j<=edg2;++j)
		{	for(int k=0;k<n1;++k) if(i&(1<<k)) m_max(f[i][j],f[i^(1<<k)][j]);
			for(int k=0;k<n2;++k) if(j&(1<<k)) m_max(f[i][j],f[i][j^(1<<k)]);
			if(sum[1][i]==sum[2][j]) ++f[i][j];
		}
}
int main()
{	read(n1);for(int i=1;i<=n1;++i) read(A[1][i]);
	read(n2);for(int i=1;i<=n2;++i) read(A[2][i]);
	edg1=(1<<n1)-1;edg2=(1<<n2)-1;
	init();dp();
	printf("%d",n1+n2-(f[edg1][edg2]<<1));
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值