zoukankan      html  css  js  c++  java
  • BZOJ2679: [Usaco2012 Open]Balanced Cow Subsets

    n<=20个数,求能分成两个和相同的子集的子集数。

    枚举子集的子集,复杂度3^n,不可,考虑折半。在一种可行方案中,每个数的系数只会是0,-1,1,题目就是要求找和为0的两个子集拼起来,将其中一个子集取反就对应成两个值相同的方案。比如找到一个子集值为x,那么另一个子集的值应为-x,只要把-x这个子集的系数全部取反,就得到两个值相同的集合,对应一种方案。

    这样转换方便折半搜索的合并过程。在合并时,枚举左边的集合,把该集合对应的状态加进数组里排序,预先把右边所有状态排序后,就可以线性时间内比较相同。若是没有转换,则需要找和为0的两个状态,麻烦!复杂度6^(n/2)。

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 #include<algorithm>
     5 //#include<iostream>
     6 using namespace std;
     7 
     8 int n,half;
     9 #define maxn 100011
    10 int first[maxn],list[maxn],next[maxn],cntl=0;
    11 struct Right
    12 {
    13     int v,S;
    14     bool operator < (const Right &b) const {return v<b.v;}
    15 }r[maxn];int cntr=0;
    16 bool vis[1100011];
    17 int a[23];
    18 int tmp[maxn],lt=0;
    19 void dfsl(int p,int v,int S)
    20 {
    21     if (p==half+1)
    22     {
    23         list[++cntl]=v;
    24         next[cntl]=first[S];
    25         first[S]=cntl;
    26         return;
    27     }
    28     dfsl(p+1,v,S);
    29     dfsl(p+1,v+a[p],S|(1<<(p-1)));
    30     dfsl(p+1,v-a[p],S|(1<<(p-1)));
    31 }
    32 void dfsr(int p,int v,int S)
    33 {
    34     if (p==n+1)
    35     {
    36         r[++cntr].v=v;
    37         r[cntr].S=S;
    38         return;
    39     }
    40     dfsr(p+1,v,S);
    41     dfsr(p+1,v+a[p],S|(1<<(p-1)));
    42     dfsr(p+1,v-a[p],S|(1<<(p-1)));
    43 }
    44 int main()
    45 {
    46     scanf("%d",&n);half=n/2;
    47     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    48     memset(first,0,sizeof(first));
    49     dfsl(1,0,0);dfsr(half+1,0,0);
    50     sort(r+1,r+1+cntr);
    51     memset(vis,0,sizeof(vis));
    52     for (int i=0;i<(1<<half);i++)
    53     {
    54         lt=0;
    55         for (int j=first[i];j;j=next[j]) tmp[++lt]=list[j];
    56         sort(tmp+1,tmp+1+lt);
    57         int k=1;
    58         for (int j=1;j<=cntr;j++)
    59         {
    60             while (k<=lt && tmp[k]<r[j].v) k++;
    61             if (k==lt+1) break;
    62             if (tmp[k]==r[j].v) vis[i|r[j].S]=1;
    63         }
    64     }
    65     int ans=0;
    66     for (int i=1;i<(1<<n);i++) if (vis[i]) ans++;
    67     printf("%d
    ",ans);
    68     return 0;
    69 }
    View Code

    注意不要把0算进去。。。。。。

  • 相关阅读:
    C#操作REDIS例子
    A C# Framework for Interprocess Synchronization and Communication
    UTF8 GBK UTF8 GB2312 之间的区别和关系
    开源项目选型问题
    Mysql命令大全——入门经典
    RAM, SDRAM ,ROM, NAND FLASH, NOR FLASH 详解(引用)
    zabbix邮件报警通过脚本来发送邮件
    centos启动提示unexpected inconsistency RUN fsck MANUALLY
    rm 或者ls 报Argument list too long
    初遇Citymaker (六)
  • 原文地址:https://www.cnblogs.com/Blue233333/p/7349171.html
Copyright © 2011-2022 走看看