Luogu3067 平衡的奶牛群 Meet in the middle
阿新 • • 發佈:2018-11-10
題意:給出$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 }