[BZOJ] 2064: 分裂

注意到\(n\)很小,应该是状压DP

记原集合为\(S\),目标集合为\(T\),如果我们能把\(S\)分成\(x\)个不相交的非空子集,且这\(x\)个子集能和\(T\)中的一些不相交非空子集的和相等,那么最终答案就是\(n+m-2x\),其中\(n=|S|,m=|T|\)

因此我们要最大化\(x\),这就是DP的目标了

\(f[x][y]\)表示\(S\)的子集\(x\)\(T\)的子集\(y\)\(sum\)相等的最多能配几对

\(sum[x]\not=sum[y]\),枚举删去\(x\)\(y\)的一个元素

\[f[x][y]=max\{f[x\oplus i][y],f[x][y\oplus j]\} \]

\(sum[x]=sum[y]\),情况看起来复杂了

为了转移,我们似乎要再枚举子集,复杂度不能接受

但是这样想,既然\(sum[x]=sum[y]\),那么\(x\)\(y\)至少配成了一对,若在其中删去一个元素,一定会减少一对

因此

\[f[x][y]=1+max\{f[x\oplus i][y],f[x][y\oplus j]\} \]

这样省去了枚举子集,复杂度有保障

最终复杂度\(O((n+m)2^{n+m})\)

骚操作:如何统计\(sum[x]\)

以前一直写

for(int S=0;S<(1<<n);S++)
    for(int i=0;i<n;i++)
        if(S&(1<<i))sum[S]+=a[i];

这样做是\(O(n2^n)\)的,实际上,完全可以省去一个\(O(n)\) (虽然\(n\)很小..)

类似统计二进制中1的个数一样,可以复用\(S\)的子集,方法就是用lowbit

也就是\(sum[x]=sum[x\oplus lowbit(x)]+sum[lowbit(x)]\)

边界是\(sum[1<<i]=a[i+1]\)

这样是\(O(2^n)\)

 
#include<algorithm>
#include<iostream>
#include<cstdio>
 
using namespace std;
 
inline int rd(){
  int ret=0,f=1;char c;
  while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
  while(isdigit(c))ret=ret*10+c-'0',c=getchar();
  return ret*f;
}
#define space putchar(' ')
#define nextline putchar('\n')
void _(int x){if(!x)return;_(x/10);putchar('0'+x%10);}
void out(int x){if(!x)putchar('0');_(x);}
 
const int MAXN = 10;
 
inline void upmax(int &x,int y){x=max(x,y);}
 
int a[MAXN],b[MAXN];
int f[1<<MAXN][1<<MAXN];
int sum1[1<<MAXN],sum2[1<<MAXN];
int n,m;
 
int main(){
  n=rd();
  for(int i=1;i<=n;i++)sum1[1<<(i-1)]=a[i]=rd();
  m=rd();
  for(int i=1;i<=m;i++)sum2[1<<(i-1)]=b[i]=rd();
  for(int i=1;i<(1<<n);i++){
    sum1[i]=sum1[i^(i&-i)]+sum1[i&-i];
  }
  for(int i=1;i<(1<<m);i++){
    sum2[i]=sum2[i^(i&-i)]+sum2[i&-i];
  }
  for(int s=1;s<(1<<n);s++){
    for(int t=1;t<(1<<m);t++){
      for(int i=0;i<n;i++){
        if(s&(1<<i)) upmax(f[s][t],f[s^(1<<i)][t]);
      }
      for(int i=0;i<m;i++){
        if(t&(1<<i)) upmax(f[s][t],f[s][t^(1<<i)]);
      }
      if(sum1[s]==sum2[t])f[s][t]++;
    }
  }
  cout<<n+m-2*f[(1<<n)-1][(1<<m)-1];
  return 0;
}

posted @ 2018-10-17 10:12  GhostCai  阅读(158)  评论(0编辑  收藏  举报