Divide and conquer:Subset(POJ 3977)
题目大意:给定一串数字序列,要你从中挑一定个数的数字使这些数字和绝对值最小,求出最小组合数
题目的数字最多35个,一看就是要数字枚举了,但是如果直接枚举,复杂度就是O(2^35)了,显然行不通,所以我们把它的组合拆成两半(前n/2个数字和后n-n/2个数字),然后给前部分和或者后部分和的组合排序,然后再用另一半在其二分查找,看最接近的和,这就是折半枚举的思想
不过这一题有很多细节:
1.和是不固定的,要左右各查找1个,如果有重复元素,一定要越过重复元素再找(lower_bound和upper_bound找就好了),同时还要防止空子列
2.这一题要找的数组合数最小的和个组合,所以排序的那一个组合,当两个组合的和一样时,要按照nums的升序排列,这样也会让1的查找顺利
1 #include <iostream> 2 #include <algorithm> 3 #include <functional> 4 5 using namespace std; 6 7 typedef long long LL_INT; 8 static struct _set 9 { 10 int nums; 11 LL_INT sum; 12 bool operator <(const _set&x) const 13 { 14 if (sum != x.sum) 15 return sum < x.sum; 16 else 17 return nums < x.nums;//如果相等则按nums排序,那样只用看upper_bound就可以了 18 } 19 }sum_set1[262149], sum_set2[262149]; 20 static LL_INT input[36]; 21 static int search_bound[3] = { -1, 0 }; 22 23 LL_INT ABS(LL_INT); 24 int Min(const int, const int); 25 void Solve(const int, LL_INT &, int &); 26 struct _set *Binary_Search_Lower(const int, LL_INT); 27 struct _set *Binary_Search_Upper(const int, LL_INT); 28 29 int main(void) 30 { 31 int ans2, n; 32 LL_INT ans1; 33 34 while (~scanf("%d",&n)) 35 { 36 if (n == 0)break; 37 for (int i = 0; i < n; i++) 38 scanf("%lld", &input[i]); 39 40 for (int i = 0; i < 1 << (n / 2); i++)//枚举左半边元素 41 { 42 sum_set1[i].nums = 0; sum_set1[i].sum = 0; 43 for (int pos = 0; pos < n / 2; pos++) 44 { 45 if (((i >> pos) & 1) == 1) 46 { 47 sum_set1[i].sum += input[pos]; 48 sum_set1[i].nums++; 49 } 50 } 51 } 52 for (int i = 0; i < 1 <<(n - (n / 2)); i++)//枚举右半边元素 53 { 54 sum_set2[i].nums = 0; sum_set2[i].sum = 0; 55 for (int pos = 0; pos < (n - (n / 2)); pos++) 56 { 57 if (((i >> pos) & 1) == 1) 58 { 59 sum_set2[i].sum += input[pos + n / 2]; 60 sum_set2[i].nums++; 61 } 62 } 63 } 64 sort(sum_set2, sum_set2 + (1 << (n - (n / 2)))); 65 Solve(n, ans1, ans2); 66 printf("%lld %d\n", ans1, ans2); 67 } 68 return EXIT_SUCCESS; 69 } 70 71 LL_INT ABS(LL_INT x) 72 { 73 return x > 0 ? x : -x; 74 } 75 76 int Min(const int x, const int y) 77 { 78 return x > y ? y : x; 79 } 80 81 void Solve(const int n, LL_INT &ans1, int &ans2) 82 { 83 struct _set *pos = NULL, *pos_up = NULL; 84 int tmp_pos, up; 85 ans1 = LLONG_MAX; ans2 = -1; 86 87 for (int i = 0; i < 1 << (n / 2); i++) 88 { 89 pos = Binary_Search_Lower(1 << (n - (n / 2)), -sum_set1[i].sum); 90 pos_up = Binary_Search_Upper(1 << (n - (n / 2)), -sum_set1[i].sum); 91 //一定要记得在找到的范围附近再寻找看还有没有绝对值更接近的 92 for (int j = 0; j < 2; j++) 93 { 94 tmp_pos = (int)(pos - sum_set2) + search_bound[j]; 95 if (0 <= tmp_pos && tmp_pos < 1 << (n - (n / 2)) 96 && (sum_set2[tmp_pos].nums || i)//不同时为0(避免空子串) 97 ) 98 { 99 if (ABS(sum_set2[tmp_pos].sum + sum_set1[i].sum) < ans1) 100 { 101 ans1 = ABS(sum_set2[tmp_pos].sum + sum_set1[i].sum); 102 ans2 = sum_set2[tmp_pos].nums + sum_set1[i].nums; 103 } 104 else if (ABS(sum_set2[tmp_pos].sum + sum_set1[i].sum) == ans1) 105 ans2 = Min(sum_set2[tmp_pos].nums + sum_set1[i].nums, ans2); 106 } 107 } 108 up = (int)(pos - sum_set2) + 1; 109 if (sum_set2[up].nums || i)//避免空子串,导致后面失效 110 { 111 if (ABS(sum_set2[up].sum + sum_set1[i].sum) < ans1) 112 { 113 ans1 = ABS(sum_set2[up].sum + sum_set1[i].sum); 114 ans2 = sum_set2[up].nums + sum_set1[i].nums; 115 } 116 else if (ABS(sum_set2[up].sum + sum_set1[i].sum) == ans1) 117 ans2 = Min(sum_set2[up].nums + sum_set1[i].nums, ans2); 118 } 119 up = (int)(pos_up - sum_set2); 120 if (sum_set2[up].nums || i)//不同时为0(避免空子串) 121 { 122 if (ABS(sum_set2[up].sum + sum_set1[i].sum) < ans1) 123 { 124 ans1 = ABS(sum_set2[up].sum + sum_set1[i].sum); 125 ans2 = sum_set2[up].nums + sum_set1[i].nums; 126 } 127 else if (ABS(sum_set2[up].sum + sum_set1[i].sum) == ans1) 128 ans2 = Min(sum_set2[up].nums + sum_set1[i].nums, ans2); 129 } 130 } 131 } 132 133 struct _set *Binary_Search_Lower(const int n, LL_INT sum1) 134 { 135 int lb = 0, mid, count1 = n, count2; 136 while (count1 > 0) 137 { 138 count2 = count1 >> 1; 139 mid = lb + (count1 >> 1); 140 if (sum_set2[mid].sum < sum1) 141 { 142 lb = ++mid; 143 count1 -= count2 + 1; 144 } 145 else count1 = count2; 146 } 147 return &sum_set2[lb]; 148 } 149 150 struct _set *Binary_Search_Upper(const int n, LL_INT sum1) 151 { 152 int lb = 0, mid, count1 = n, count2; 153 while (count1 > 0) 154 { 155 count2 = count1 >> 1; 156 mid = lb + (count1 >> 1); 157 if (sum_set2[mid].sum <= sum1) 158 { 159 lb = ++mid; 160 count1 -= count2 + 1; 161 } 162 else count1 = count2; 163 } 164 return &sum_set2[lb]; 165 }
最后尼玛,我wa很多次,发现原来是我的min函数写错了。。。写成了max函数。。。。。
参考了一下http://www.cnblogs.com/hyxsolitude/p/3642053.html