Divide and conquer:Sumsets(POJ 2549)

                

                  数集

  题目大意:给定一些数的集合,要你求出集合中满足a+b+c=d的最大的d(每个数只能用一次)

  这题有两种解法,

  第一种就是对分,把a+b的和先求出来,然后再枚举d-c,枚举的时候输入按照降序搜索就好,一旦d满足条件就是最大的了,另外判断不重复存一下位置就好,时间复杂度0(n^2*logn)

  

 1 #include <iostream>
 2 #include <functional>
 3 #include <algorithm>
 4
 5 using namespace std;
 6
 7 typedef long long LL_INT;
 8 static LL_INT input[1001];
 9 static pair<LL_INT, pair<int,int>>sums[1001 * 1001];
10
11 LL_INT solve(const int, const int);
12
13 bool cmp(const pair<LL_INT, pair<int, int>>&x, const pair<LL_INT, pair<int, int>>&y)
14 {
15     return x.first < y.first;
16 }
17
18 int main(void)
19 {
20     LL_INT ans;
21     int num_sum, sum_comb;
22     while (~scanf("%d", &num_sum))
23     {
24         if (num_sum == 0) break;
25         for (int i = 0; i < num_sum; i++)
26             scanf("%lld", &input[i]);
27
28         sort(input, input + num_sum);
29         sum_comb = 0;
30         for (int i = 0; i < num_sum; i++)
31             for (int j = i + 1; j < num_sum; j++)
32                 sums[sum_comb++] = make_pair(input[i] + input[j], make_pair(i, j));
33
34         sort(sums, sums + sum_comb);
35         ans = solve(num_sum, sum_comb);
36         if (ans != INT_MAX)
37             printf("%lld\n", ans);
38         else
39             printf("no solution\n");
40     }
41     return EXIT_SUCCESS;
42 }
43
44 LL_INT solve(const int num_sum, const int sum_comb)
45 {
46     int tmp[2] = { 0, 0 }, pos_s, pos_up;
47     for (int i = num_sum - 1; i >= 0; i--)
48     {
49         for (int j = num_sum - 1; j >= 0; j--)
50         {
51             if (i == j) continue;
52             pos_s = lower_bound(sums, sums + sum_comb, make_pair(input[i] - input[j], make_pair(0, 0)),cmp) - sums;
53             pos_up = upper_bound(sums, sums + sum_comb, make_pair(input[i] - input[j], make_pair(0, 0)),cmp) - sums;
54
55             if (sums[pos_s].first == input[i] - input[j])
56             {
57                 for (int k = pos_s; k < pos_up; k++)
58                 {
59                     if (sums[k].second.first != i && sums[k].second.first != j
60                         &&sums[k].second.second != i && sums[k].second.second != j)
61                         return input[i];
62                 }
63             }
64         }
65     }
66     return (LL_INT)INT_MAX;
67 }

  

  剪枝了还是100+ms,太慢了,我不是很满意,去网上找了下果然有更快的做法

  其实像这种找固定数的题目都可以用散列来做,把a+b散列就好了,说实话好久没做散列我都忘记散列的那几条挺好用的公式了(貌似用书上那种散列方法效率更高),这里我就直接用链表法解决了

  参考http://www.cnblogs.com/CSU3901130321/p/4546190.html

  

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <functional>
 4 #define MOD 1001 * 1001
 5
 6 using namespace std;
 7
 8 static struct _set
 9 {
10     int val, num[2], next;
11 }memmory_pool[1001 * 1001];
12 static int Table[1001 * 1001], input[1001], tp;
13
14 void Hash_To_Table(const int, const int, const int);
15 bool Search_Table(const int, const int, const int);
16 int solve(const int);
17
18 int main(void)
19 {
20     int num_sum, sum_comb, ans;
21     while (~scanf("%d", &num_sum))
22     {
23         if (num_sum == 0)break;
24         for (int i = 0; i < num_sum; i++)
25             scanf("%d", &input[i]);
26         sort(input, input + num_sum);
27         sum_comb = num_sum*(num_sum - 1) / 2; tp = 0;
28
29         fill(Table, Table + 1001 * 1001, -1);
30         for (int i = 0; i < num_sum; i++)
31             for (int j = i + 1; j < num_sum; j++)
32                 Hash_To_Table(input[i] + input[j], i, j);
33
34         ans = solve(num_sum);
35         if (ans != INT_MAX)
36             printf("%d\n", ans);
37         else
38             printf("no solution\n");
39     }
40     return EXIT_SUCCESS;
41 }
42
43 void Hash_To_Table(const int _int_value, const int i, const int j)
44 {
45     int pos = (_int_value > 0 ? _int_value : -_int_value) % MOD;
46     memmory_pool[tp].val = _int_value;
47     memmory_pool[tp].num[0] = i;
48     memmory_pool[tp].num[1] = j;
49     memmory_pool[tp].next = Table[pos];
50     Table[pos] = tp++;
51 }
52
53 bool Search_Table(const int _int_value, const int i, const int j)
54 {
55     int pos = (_int_value > 0 ? _int_value : -_int_value) % MOD;
56     for (int k = Table[pos]; k != -1; k = memmory_pool[k].next)
57     {
58         if (memmory_pool[k].val == _int_value)
59         {
60             if (memmory_pool[k].num[0] != i &&memmory_pool[k].num[1] != i
61                 &&memmory_pool[k].num[0] != j &&memmory_pool[k].num[1] != j)
62                 return true;
63         }
64     }
65     return false;
66 }
67
68 int solve(const int num_sum)
69 {
70     for (int i = num_sum - 1; i >= 0; i--)
71     {
72         for (int j = num_sum - 1; j >= 0; j--)
73         {
74             if (i == j)continue;
75             if (Search_Table(input[i] - input[j], i, j))
76                 return input[i];
77         }
78     }
79     return INT_MAX;
80 }

  

  散列法复杂度是O(N^2),非常快。

  (另外有人在讨论版说他用O(n^3*logn)的时间复杂度方法94ms做出来了,我自己试了一下,如果他储存位置下标位置是一定会溢出的,不知道他怎么做出来的,而且就算是O(N^2*logn)的算法也是要用100+ms,我对他的做法表示怀疑)。

时间: 2024-10-12 17:05:37

Divide and conquer:Sumsets(POJ 2549)的相关文章

Divide and conquer:Median(POJ 3579)

    快速求两数距离的中值 题目大意:给你一个很大的数组,要你求两个数之间的距离的中值 二分法常规题,一个pos位就搞定的事情 1 #include <iostream> 2 #include <algorithm> 3 #include <functional> 4 5 using namespace std; 6 typedef long long LL_INT; 7 8 static int nums[100002]; 9 bool judge(LL_INT,

Divide and conquer:Matrix(POJ 3685)

矩阵 题目大意:矩阵里面的元素按i*i + 100000 * i + j*j - 100000 * j + i*j填充(i是行,j是列),求最小的M个数 这一题要用到两次二分,实在是二分法的经典,主要是每一列都是按照行来递增的,每一行我们都用二分法找到比mid小的那些数就好了 参考http://blog.csdn.net/nw4869/article/details/24124019 1 #include <iostream> 2 #include <algorithm> 3 #i

poj 2549 --- 传说中的用“桶”防止HASH冲突

http://poj.org/problem?id=2549 维基百科有3Sum算法:https://en.wikipedia.org/wiki/3SUM sort(S); for i=0 to n-3 do a = S[i]; k = i+1; l = n-1; while (k<l) do b = S[k]; c = S[l]; if (a+b+c == 0) then output a, b, c; // Continue search for all triplet combinatio

分治算法学习 Divide and Conquer

分治思想: 分治算法的思想就是 对于某些特定种类的问题  如果问题的规模很小,那么就直接解决,如果问题的规模比较大,那么就把问题先分解为规模小的但是问题相同的子问题 ,并且不断分解直到规模足够小,再递归地解决这些问题 如果原问题可分割成k个子问题,1<k≤n,且这些子问题都可解并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行的. 递归与分治经常是一起使用的 能够用分治的情况 : 1.问题复杂性随规模减小而减小 2.问题具有最优子结构性质        最优子结构:如果问题的最优解所包

Divide and conquer:Telephone Lines(POJ 3662)

电话线 题目大意:一堆电话线要你接,现在有N个接口,总线已经在1端,要你想办法接到N端去,电话公司发好心免费送你几段不用拉网线,剩下的费用等于剩余最长电话线的长度,要你求出最小的费用. 这一看又是一个最小化最大值的问题(也可以看成是最大化最小值的问题),常规方法一样的就是把这个费用二分就好,但是这道题是道图论题,不一定经过所有的点,那我们就以二分基准长度为界限,把小于基准长度的那一部分看成是0,大于等于基准长度的看成是1,这样我们只用SPFA算法算最短路径就可以了,非常的巧妙 参考:http:/

POJ 2549 Sumsets

Sumsets Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10593   Accepted: 2890 Description Given S, a set of integers, find the largest d such that a + b + c = d where a, b, c, and d are distinct elements of S. Input Several S, each cons

Divide and conquer:Aggressive Cows(POJ 2456)

侵略性的牛 题目大意:C头牛最大化他们的最短距离 常规题,二分法即可 1 #include <iostream> 2 #include <algorithm> 3 #include <functional> 4 5 using namespace std; 6 7 static int pos[100000]; 8 9 bool judge(const int, const int,const int); 10 void solve(const int, const i

Divide and conquer:Monthly Expense(POJ 3273)

Monthly Expense 题目大意:不废话,最小化最大值 还是直接套模板,不过这次要注意,是最小化最大值,而不是最大化最小值,判断的时候要注意 联动3258 1 #include <iostream> 2 #include <functional> 3 #include <algorithm> 4 5 using namespace std; 6 7 static int money_set[100010]; 8 9 void Search(const int,

Divide and Conquer:River Hopscotch(POJ 3258)

 去掉石头 题目大意:一群牛在河上的石头上跳来跳去,现在问你如何通过去掉M个石头,使得牛跳过石头的最短距离变得最大? 这一题比较经典,分治法的经典,二分法可以很方便处理这个问题,我们只要明白比较函数这个东西就可以了. 模板: while (……) { mid = (lb + rb) / 2; if (Judge_C(……)) else rb = mid; } while判断条件可以根据是整形还是浮点型灵活变换,Judge_C就是比较函数,几乎所有的分治算法都可以这样归纳,我们只要找到合适的比较函