1. 程式人生 > 其它 >AcWing 171. 送禮物

AcWing 171. 送禮物

題目傳送門

一、題目解析

二、實現程式碼

#include <bits/stdc++.h>

using namespace std;
const int N = 48; // 1≤N≤46
typedef long long LL;

int n;               // N個禮物
int m;               // 重量之和不超過m,上限,這是一個整數
int k;               // 前k個,即索引下標0~k-1,最大值為N/2,就當25算
int w[N];            // 每個禮物的重量
int sum[1 << N / 2]; //前半段組合可能出現的所有和
//前半部分收集到的所有和,下標因為一直在保持++狀態,所以最後一次執行完,也可以理解為前半部分陣列的個數
//在排序去重後,此變更也可以視為前半段陣列的元素個數,在二分中,因為需要使用的是索引號:0~cnt-1
int cnt;
int ans; //最大重量

// u:第幾號禮物,下標從0開始
// s:本路線上累加的禮物物理和
void dfs1(int u, int s) {
    if (u == k) {       //如果能夠到達第k個下標位置,表示前面0~k-1共k個選擇完畢
        sum[cnt++] = s; //記錄禮物重量和
        return;
    }

    //放棄u號物品,走到下一個u+1號面前
    dfs1(u + 1, s);

    //如果加上u號物品重量,不會超過上限m,那麼可以選擇
    if ((LL)s + w[u] <= m) dfs1(u + 1, s + w[u]);
}

//後半部分
void dfs2(int u, int s) {
    if (u == n) {
        int l = 0, r = cnt - 1;
        //利用二分模板2,找出<=m的右邊界
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if ((LL)sum[mid] + s <= m)
                l = mid;
            else
                r = mid - 1;
        }
        //更新更大的重量
        ans = max(ans, sum[l] + s);
        return;
    }
    //放棄當前禮物
    dfs2(u + 1, s);
    //如果加上u號物品重量,不會超過上限m,那麼可以選擇
    if ((LL)s + w[u] <= m) dfs2(u + 1, s + w[u]);
}
int main() {
    //先讀入m再讀入n,別整反了
    cin >> m >> n;
    for (int i = 0; i < n; i++) cin >> w[i]; //每個禮物重量
    //由大到小排序
    sort(w, w + n, greater<int>());

    //一家一半
    k = n / 2;

    //前面開始搜尋 0~k-1
    // dfs1,枚舉出了所有可能出現的組合值
    dfs1(0, 0);

    //結果排序
    sort(sum, sum + cnt);

    //去重
    cnt = unique(sum, sum + cnt) - sum; //最後這個-1是換算出sum資料最後一個的下標

    //後半部分搜尋 k~n
    dfs2(k, 0);

    //輸出答案
    cout << ans << endl;
    return 0;
}