折半枚举(双向搜索)

各有n个整数的四个数列A、B、C、D。要从每个数列中各取一个数,使四个数的和为0。求出这样组合的个数。

输入

n = 6

A = { -45, -41, -36, -36, 26, -32 }

B = { 22, -27, 53, 30, -38, -54 }

C = { 42, 56, -37, -75, -10, -6 }

D = { -16, 30, 77, -46, 62, 45 }

从4个数列中选择共有n4种情况,全部判断一遍不可行。不过将它们对半分成AB和CD再考虑,就可以快速解决了。从2个数列中选择的话只有n2种组合,所以可以进行枚举。先从A、B中取出a、b后,为使总和为0需要从C、D中取出c + d = -(a + b)。因此先将从C、D中取数字的n2种方法全都枚举出来,将这些和排序,进行二分搜索。这个算法复杂度为O(n2logn)。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 using namespace std;
 5 
 6 #define MAX_N 1000
 7 
 8 int n;
 9 int A[MAX_N], B[MAX_N], C[MAX_N], D[MAX_N];
10 int CD[MAX_N * MAX_N];    //C和D中数字的组合方法
11 
12 void solve()
13 {
14     //枚举从C和D中取出数字的所有方法
15     for (int i = 0; i < n; i++)
16     {
17         for (int j = 0; j < n; j++)
18         {
19             CD[i*n+j] = C[i] + D[j];
20         }
21     }
22     sort(CD, CD+n*n);
23     long long res = 0;
24     for (int i = 0; i < n; i++)
25     {
26         for (int j = 0; j < n; j++)
27         {
28             int cd = -(A[i] + B[j]);
29             //取出C和D中和为cd的部分
30             res += upper_bound(CD, CD+n*n, cd) - lower_bound(CD, CD+n*n, cd);
31         }
32     }
33     printf("%lld\n", res);
34 }
35 
36 
37 int main()
38 {
39     cin >> n;
40     for (int i = 0; i < n * 4; i++)
41     {
42         switch (i / 6)
43         {
44         case 0:
45             cin >> A[i % 6];
46             break;
47         case 1:
48             cin >> B[i % 6];
49             break;
50         case 2:
51             cin >> C[i % 6];
52             break;
53         case 3:
54             cin >> D[i % 6];
55             break;
56         default:
57             break;
58         }
59     }
60     solve();
61     return 0;
62 }

 

posted @ 2014-10-29 11:18  bournet  阅读(592)  评论(0编辑  收藏  举报