题意:给出$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 }
原文地址:https://www.cnblogs.com/Itst/p/9813382.html
时间: 2024-10-04 21:09:19