1. 程式人生 > >Luogu3067 平衡的奶牛群 Meet in the middle

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 }