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; }