zoukankan      html  css  js  c++  java
  • 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
    ", 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

      

  • 相关阅读:
    Java实现判断单联通(强连通缩点+拓扑排序)Going from u to v or from v to u
    Java实现判断单联通(强连通缩点+拓扑排序)Going from u to v or from v to u
    Java实现判断单联通(强连通缩点+拓扑排序)Going from u to v or from v to u
    Java实现判断单联通(强连通缩点+拓扑排序)Going from u to v or from v to u
    判断一个窗体是否被完全遮挡(比较有意思,但这招有什么用呢?)
    Delphi中用MessageBox()API函数做倒计时对话框(使用Hook安装CBTHookCallback,计时器更改文字,SetWindowText API真正修改文字,引用未知函数)good
    Delphi&C#代码模拟“显示桌面”的功能(使用CreateOleObject('Shell.Application'))
    LLVM和GCC的区别(LLVM提供了模块化的编译模块,非常有利于重用,以前的编译器都没有做到这一点)
    Delphi产生任务栏图标【TNotifyIconData】
    国内外10大项目外包平台
  • 原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/5174032.html
Copyright © 2011-2022 走看看