poj 2785 4 Values whose Sum is 0 折半枚举

题目链接:http://poj.org/problem?id=2785

 

枚举的一般思路就是先把所有的状态枚举出来 最后一次性判断该状态合法不合法

而折半枚举的思想是

先枚举一半的状态 把他们的状态存起来 排序

然后再枚举剩下一般 用目标反推前一半的期望状态 接下来在前一半的结果数组中查找是否有相应结果

之所以能优化是因为结果数组有序 就可以用二分搜索

复杂度从O(n^2 * n^2) 降到 O(n^2 * log(n^2))即(O(n^2 * log n))

 

二分搜索的一个技巧

在有序数组中用二分查找方式统计有多少值为CD的元素的写法:

res += upper_bound(cd, cd + n*n, CD) - lower_bound(cd, cd + n*n, CD);

 

类似的问题还有“超大的01背包问题”

 

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
#include <set>
#include <queue>
#include <vector>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;

const int maxn = 5000;

int a[maxn], b[maxn], c[maxn], d[maxn];
int cd[maxn * maxn];

int main()
{
    //freopen("in.txt", "r", stdin);

    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
        scanf("%d%d%d%d", &a[i], &b[i], &c[i], &d[i]);

    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            cd[i*n + j] = c[i] + d[j];

    sort(cd, cd + n * n);

    ll res = 0;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            int CD = -(a[i] + b[j]);
            res += upper_bound(cd, cd + n*n, CD) - lower_bound(cd, cd + n*n, CD);
        }
    }

    printf("%lld\n", res);

    return 0;
}

 

posted @ 2015-03-04 19:21  地鼠地鼠  阅读(125)  评论(0编辑  收藏  举报