zoukankan      html  css  js  c++  java
  • [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] ot=sum[y]),枚举删去(x)(y)的一个元素

    [f[x][y]=max{f[xoplus i][y],f[x][yoplus j]} ]

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

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

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

    因此

    [f[x][y]=1+max{f[xoplus i][y],f[x][yoplus 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[xoplus 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('
    ')
    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;
    }
    
    
    未经许可,禁止搬运。
  • 相关阅读:
    Maven下载依赖项的源代码(source code)和Javadoc
    Spring读写xml文件
    重建二叉树
    从尾到头打印链表
    替换空格
    洞穴逃生
    二维数组中的查找
    分苹果
    最小生成树算法prim and kruskal
    HTTP报文格式详解
  • 原文地址:https://www.cnblogs.com/ghostcai/p/9802403.html
Copyright © 2011-2022 走看看