1. 程式人生 > 實用技巧 >HDU3017 Treasure Division 題解 折半搜尋

HDU3017 Treasure Division 題解 折半搜尋

題目連結: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;
}