Luogu3067 平衡的奶牛群 Meet in the middle

Posted itst

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu3067 平衡的奶牛群 Meet in the middle相关的知识,希望对你有一定的参考价值。

题意:给出$N$个范围在$[1,10^8]$内的整数,问有多少种取数方案使得取出来的数能够分成两个和相等的集合。$N leq 20$


 

发现爆搜是$O(3^N)$的,所以考虑双向搜索。

先把前$3^frac{N}{2}$搜完,然后每一次搜出后$3^frac{N}{2}$的时候,枚举前面的$2^frac{N}{2}$,每一个对应一下看有没有和为$0$的方案即可。复杂度为$O(6^frac{N}{2})$,虽然不开O2过不去qwq

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 inline int read(){
 5     int a = 0;
 6     char c = getchar();
 7     while(!isdigit(c))
 8     c = getchar();
 9     while(isdigit(c)){
10     a = (a << 3) + (a << 1) + (c ^ 0);
11     c = getchar();
12     }
13     return a;
14 }
15 
16 struct HashTable{
17 #define MOD 103
18     struct node{
19         int num;
20         node* nxt;
21     }*begin[MOD] , *last[MOD];
22     void insert(int num){
23         int t = num % MOD;
24         if(t < 0)
25             t += MOD;
26         if(last[t] == NULL){
27             begin[t] = new node;
28             begin[t]->num = num;
29             begin[t]->nxt = NULL;
30             last[t] = begin[t];
31         }
32         else{
33             node* now = new node;
34             now->num = num;
35             now->nxt = NULL;
36             last[t]->nxt = now;
37             last[t] = now;
38         }
39     }
40     
41     bool count(int num){
42         int t = num % MOD;
43         if(t < 0)
44             t += MOD;
45         for(node* i = begin[t] ; i != NULL ; i = i->nxt)
46             if(i->num == num)
47                 return 1;
48         return 0;
49     }
50 }zt[1 << 10];
51 int M[21] , N , ans;
52 bool is[1 << 10][1 << 10];
53 
54 void init(int now , int end , int cnt , int sum){
55     if(now > end){
56         zt[cnt].insert(sum);
57         return;
58     }
59     init(now + 1 , end , cnt , sum);
60     init(now + 1 , end , cnt | (1 << now) , sum + M[now]);
61     init(now + 1 , end , cnt | (1 << now) , sum - M[now]);
62 }
63 
64 void getAns(int now , int end , int cnt , int sum){
65     if(now > end){
66     for(int i = 0 ; i < 1 << (N >> 1) ; i++)
67         if(!is[cnt][i] && (zt[i].count(sum) || zt[i].count(-sum))){
68             is[cnt][i] = 1;
69             ans++;
70         }
71     return;
72     }
73     getAns(now + 1 , end , cnt , sum);
74     getAns(now + 1 , end , cnt | (1 << now - (N >> 1)) , sum + M[now]);
75     getAns(now + 1 , end , cnt | (1 << now - (N >> 1)) , sum - M[now]);
76 }
77 
78 int main(){
79     N = read();
80     for(int i = 0 ; i < N ; i++)
81     M[i] = read();
82     init(0 , (N >> 1) - 1 , 0 , 0);
83     getAns(N >> 1 , N - 1 , 0 , 0);
84     cout << ans - 1;
85     return 0;
86 }

再放一个复杂度似乎不对但是很快的方法

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 struct node{
 5     int zt , sum;
 6 }num1[60010] , num2[60010];
 7 int N , cnt1 , cnt2 , M[21];
 8 bool vis[2000010];
 9 
10 void dfs(node* num , int& cnt , int now , int end , int sum , int zt){
11     if(now > end){
12         num[++cnt].sum = sum;
13         num[cnt].zt = zt;
14         return;
15     }
16     dfs(num , cnt , now + 1 , end , sum , zt);
17     dfs(num , cnt , now + 1 , end , sum + M[now] , zt | (1 << now));
18     dfs(num , cnt , now + 1 , end , sum - M[now] , zt | (1 << now));
19 }
20 
21 bool cmp(node a , node b){
22     return a.sum < b.sum;
23 }
24 
25 bool operator == (node a , node b){
26     return a.zt == b.zt && a.sum == b.sum;
27 }
28 
29 int main(){
30     cin >> N;
31     for(int i = 0 ; i < N ; i++)
32         cin >> M[i];
33     dfs(num1 , cnt1 , 0 , (N - 2) >> 1 , 0 , 0);
34     dfs(num2 , cnt2 , N >> 1 , N - 1 , 0 , 0);
35     sort(num1 + 1 , num1 + cnt1 + 1 , cmp);
36     sort(num2 + 1 , num2 + cnt2 + 1 , cmp);
37     cnt1 = unique(num1 + 1 , num1 + cnt1 + 1) - num1 - 1;
38     cnt2 = unique(num2 + 1 , num2 + cnt2 + 1) - num2 - 1;
39     int p1 = cnt2 , p2 = cnt2;
40     for(int i = 1 ; i <= cnt1 ; i++){
41         while(p1 && num1[i].sum + num2[p1].sum > 0)
42             p1--;
43         p2 = p1;
44         while(p2 && num1[i].sum + num2[p2].sum >= 0)
45             p2--;
46         while(++p2 <= p1)
47             vis[num1[i].zt | num2[p2].zt] = 1;
48     }
49     int ans = 0;
50     for(int i = 1 ; i < 1 << N ; i++)
51         ans += vis[i];
52     cout << ans;
53     return 0;
54 }

 

以上是关于Luogu3067 平衡的奶牛群 Meet in the middle的主要内容,如果未能解决你的问题,请参考以下文章

折半搜索+状态压缩P3067 [USACO12OPEN]平衡的奶牛群Balanced Cow S…

题解Luogu P3052 USACO12摩天大楼里的奶牛Cows in a Skyscraper

Luogu2345 奶牛集会(树状数组)

luogu P2345 奶牛集会

[luogu2847]奶牛广播-金

luogu P2345 奶牛集会 |排序+树状数组