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

题目大意

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 + 10
typedef 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 ;
}

以上.

你可能感兴趣的:(JZOJ2935. 【USACO Open 2012 Gold Division】Balanced Cow Subsets)