JZOJ2935. 【USACO Open 2012 Gold Division】Balanced Cow Subsets

来源:互联网 发布:摩托罗拉gp328编程 编辑:程序博客网 时间:2024/06/01 08:30

题目大意

n个数,从中任意选出一些数,使这些数能分成和相等的两组。
求有多少种选数的方案。

Data Constraint
n20

题解

这题用到了 meet in the middle 的思想,或者说是折半搜索。
先将n分为两半,每一半O(3n2)枚举每一个数的系数1/0/1。然后把式子写出来可以发现,当左边的和=右边的和的时候,就是用左边1与右边1分在一组。然后暴力维护一下,去重。

SRC

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>using namespace std ;#define N 20 + 10#define M 2000 + 10#define K 60000 + 10typedef long long ll ;struct Stype {    ll x ;    ll s ;} s1[K] , s2[K] ;bool vis[M][M] ;ll a[N] ;ll c[K] , d[K] ;int n , tot1 , tot2 , Sign = 0 ;ll ans ;bool cmp( Stype a , Stype b ) { return a.s < b.s ; }void DFS( int k , int m , ll sum , ll sta ) {    if ( k > m ) {        if ( !Sign ) s1[++tot1].x = sta , s1[tot1].s = sum ;        else s2[++tot2].x = sta , s2[tot2].s = sum ;        return ;    }    for (int c = -1 ; c <= 1 ; c ++ )        DFS( k + 1 , m , sum + a[k] * c , sta | ((ll)(c != 0) << (k - Sign - 1)) ) ;}void Calc() {    for (int i = 1 ; i <= c[0] ; i ++ ) {        for (int j = 1 ; j <= d[0] ; j ++ ) {            if ( vis[c[i]][d[j]] ) continue ;            vis[c[i]][d[j]] = 1 ;            ans ++ ;        }    }}int main() {    scanf( "%d" , &n ) ;    for (int i = 1 ; i <= n ; i ++ ) scanf( "%lld" , &a[i] ) ;    DFS( 1 , n / 2 , 0 , 0 ) ;    Sign = n / 2 ;    DFS( n / 2 + 1 , n , 0 , 0 ) ;    sort( s1 + 1 , s1 + tot1 + 1 , cmp ) ;    sort( s2 + 1 , s2 + tot2 + 1 , cmp ) ;    if ( s1[1].s < s2[1].s ) swap( s1 , s2 ) , swap( tot1 , tot2 ) ;    int j = 1 ;    vis[0][0] = 1 ;    for (int i = 1 ; i <= tot1 && j <= tot2 ; ) {        while ( s1[i].s > s2[j].s && j <= tot2 ) j ++ ;        if ( j > tot2 ) break ;        if ( s1[i].s < s2[j].s ) { i ++ ; continue ; }        c[0] = d[0] = 1 ;        c[1] = s1[i].x ;        d[1] = s2[j].x ;        int k ;        for (k = i + 1 ; k <= tot1 ; k ++ ) {            if ( s1[k].s != s1[i].s ) { k -- ; break ; }            c[++c[0]] = s1[k].x ;        }        i = k + 1 ;        for (k = j + 1 ; k <= tot2 ; k ++ ) {            if ( s2[k].s != s2[j].s ) { k -- ; break ; }            d[++d[0]] = s2[k].x ;        }        j = k + 1 ;        Calc() ;    }    printf( "%lld\n" , ans ) ;    return 0 ;}

以上.

2 0
原创粉丝点击