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

  

你可能感兴趣的:(Divide and conquer:Subset(POJ 3977))