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;
}
posted @ 2020-09-18 23:27  quanjun  阅读(116)  评论(0编辑  收藏  举报