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