Problem
找到一個完備集合,其任兩個數字做 AND 和 OR 運算都在集合中。
給定部分集合,請問至少還需要幾個元素使得其變成完備集合。
| 1 2 3 4 5 6 7 8 9
 | 4 5 0 1 3 5 7 2 2 4 3 3 7 11 3 1 2 4
 | 
Sample Output
| 1 2 3 4
 | Case #1: 0 Case #2: 2 Case #3: 1 Case #4: 5
 | 
Solution
這一題從傳錯題目開始已經放著很久,當時怎麼樣都沒有想法,直接著手 O(n * 2^20) 最慘運行方式,其實這種複雜度與通過的複雜度就差那麼一點的 n。 // 肯定是死在 n。
其實可以發現到,假使挑選 i-th bit 很有可能會連帶其他的 bit 一同帶上,因此對於 i-th bit 最小化攜帶的 bit (做 AND 最小化)。
最後窮舉攜帶 i-th bit 是否要挑 (OR 所有攜帶的 bit),複雜度降至 O(2^20)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
 | #include <stdio.h> #include <math.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <map> #include <set> #include <string.h> #include <assert.h> using namespace std; int main() {     int testcase, cases = 0;     int n, x;     scanf("%d", &testcase);     while (testcase--) {         scanf("%d", &n);         set<int> S;         int A[32] = {}, all = (1<<18) - 1;          memset(A, -1, sizeof(A));         for (int i = 0; i < n; i++) {             scanf("%d", &x);             S.insert(x);             all &= x;              for (int j = 0; j < 18; j++) {                 if ((x>>j)&1) {                     if (A[j] == -1)                         A[j] = x;                     else                         A[j] = A[j] & x;                 }             }         }         S.insert(all);         int m = 0;         for (int i = 0; i < 18; i++) {             if (A[i] != -1)                 A[m++] = A[i];         }         for (int i = 1; i < (1<<m); i++) {              int num = 0;             for (int j = 0; j < m; j++) {                 if ((i>>j)&1) {                     num |= A[j];                 }             }             S.insert(num);         }         printf("Case #%d: %d\n", ++cases, (int)S.size() - n);     }     return 0; }
 |