标签:
虽然数据小,但此题乍一看不可做。
于是我仔(cha)细(yue)思(ti)考(jie)。
有一种方法肯定是可以的,把以前的全部合并,再一个个分裂成现在的。
那么最少的方案怎么来呢?
如果以前的一部分可以单独对应(单独地合并分裂)到现在的一部分,就没必要把他们和剩下的再合到一起再分开了。
这个过程用dp解决。
为了方便,另以前的面积为正,现在的面积为负,于是就是要尽量的凑零。
于是我们要知道所有的选择的和。
这个用状压解决,另sum[S]表示把S对应的集合(转为二进制)的面积之和。
sum可以递推,用S&-S求出最小元素t,然后sum[S]=sum[t]+sum[S-t],常用技巧get。
f[S]表示S这个集合处理过程中最多能得到0的次数。
最优子结构,枚举每一个i∈S,f[S]=max(f[S^i])。
如果sum[S]=0,f[S]++。
最后答案就是n+m-2*f[Smax]。
n+m是最原始的做法,*2是一个0可以少一次合并一次分裂。
感觉也是一道不错的题。
#include<cstdio> #include<algorithm> using namespace std; const int maxn=20; int f[1<<maxn],sum[1<<maxn]; int main(){ int n,m,x; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&sum[1<<i]); scanf("%d",&m); for(int i=n;i<n+m;i++){ scanf("%d",&x); sum[1<<i]=-x; } int end=1<<(n+m); for(int i=1;i<end;i++){ int t=i&-i; sum[i]=sum[t]+sum[i-t]; for(int j=0;j<n+m;j++) if(i&(1<<j)) f[i]=max(f[i],f[i^(1<<j)]); if(!sum[i]) f[i]++; } printf("%d",n+m-2*f[end-1]); return 0; }
标签:
原文地址:http://www.cnblogs.com/xkui/p/4540093.html