子集:从S中找出子集{a,b,c,d}使得 a + b + c = d且d最大。
3.2常用技巧精选(一)
折半枚举
睡不着,A一题提提神。子集最多有1000C4*4个,时限只有一秒。折半枚举有些讲究,以前折半都是在集合上折半,这次是在等式上。将等式变化为 a + b = d – c,预先算好左右两边并排序。左边全枚举N2,右边二分查找logN2,每枚举一次左边都会在右边二分常数次,所以总的复杂度是O(N2logN2):
#include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; #define MAX_N 1000 + 16 typedef long long LL; struct Half { LL v; int i, j; Half(LL v, int i, int j) : v(v), i(i), j(j){} bool operator < (const Half& other) const { return v < other.v; } bool operator > (const Half& other) const { return v > other.v; } bool operator != (const Half& other) const { return i != other.i && j != other.j && i != other.j && j != other.i; } }; LL S[MAX_N]; ///////////////////////////SubMain////////////////////////////////// int main(int argc, char *argv[]) { #ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif int n; while (cin >> n && n) { for (int i = 0; i < n; ++i) { cin >> S[i]; } vector<Half> left, right; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { left.push_back(Half(S[i] + S[j], i, j)); } } for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { right.push_back(Half(S[i] - S[j], i, j)); right.push_back(Half(S[j] - S[i], j, i)); } } sort(left.begin(), left.end(), greater<Half>()); sort(right.begin(), right.end()); LL result; memset(&result, 0x80, sizeof(LL)); for (vector<Half>::const_iterator it = left.begin(); it != left.end(); ++it) { vector<Half>::iterator lb = lower_bound(right.begin(), right.end(), *it); vector<Half>::iterator ub = upper_bound(lb, right.end(), *it); for (; lb != ub; ++lb) { if (*lb != *it) { result = max(result, it->v + S[lb->j]); } } } if (result < -536870912) { cout << "no solution" << endl; } else { cout << result << endl; } } #ifndef ONLINE_JUDGE fclose(stdin); fclose(stdout); system("out.txt"); #endif return 0; } ///////////////////////////End Sub//////////////////////////////////
12904640 | hankcs | 2549 | Accepted | 19948K | 719MS | C++ | 1972B | 2014-05-22 05:18:24 |