HDU3017 Treasure Division 題解 折半搜尋
阿新 • • 發佈:2020-09-19
題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=3017
題目大意:
有 \(n(\le 30)\) 塊硬幣(\(n\) 可能是奇數),每塊硬幣都有一個幣值。要求將 \(n\) 塊金幣分成兩堆,使得兩堆硬幣幣值和的差儘可能地小。輸出這個最小的差。
解題思路:
暴力搜尋的時間複雜度為 \(O(2^{30})\),會超時,所以考慮使用折半搜尋,時間複雜度降為 \(O(2 \cdot 15 \cdot 2^{15})\)。
示例程式碼:
#include <bits/stdc++.h> using namespace std; int n; long long v[31], ans; set<long long> st[16]; void dfs1(int id, int cnt, long long tmp) { if (id > n/2) { st[cnt].insert(tmp); return; } dfs1(id+1, cnt, tmp-v[id]); dfs1(id+1, cnt+1, tmp+v[id]); } void dfs2(int id, int cnt, long long tmp) { if (id > n) { int p = n/2 - max(0, n/2 - cnt); assert(p >= 0 && p <= n/2); set<long long>::iterator it = st[p].lower_bound(tmp); if (it != st[p].end()) { int val = *it; ans = min(ans, abs(tmp - val)); } if (it != st[p].begin()) { it --; int val = *it; ans = min(ans, abs(tmp - val)); } return; } dfs2(id+1, cnt, tmp-v[id]); dfs2(id+1, cnt+1, tmp+v[id]); } int main() { while (~scanf("%d", &n)) { for (int i = 0; i <= n/2; i ++) st[i].clear(); ans = (1LL<<60); for (int i = 1; i <= n; i ++) scanf("%lld", v+i); dfs1(1, 0, 0); dfs2(n/2+1, 0, 0); printf("%lld\n", ans); } return 0; }