$\newcommand{\O}{\mathrm{O}}$ My Algorithm : kopricky アルゴリズムライブラリ

kopricky アルゴリズムライブラリ

01 Knapsack Branch and Bound

コードについての説明

01ナップサック問題の分割統治法を用いた解法. yukicoder のこれで出題されたもので愚直に $\O (nW)$ の DP ができないような制約に対して効果的である.
このスライド とかを見るとよいかも. 単純に 「価値 / 重さ」 の降順に並べて分割統治法を行っている.

(関数)
solve$(n, W, vec)$ : $n$ 個の商品 vec(価値, 重さ) を用いて, 重さ $W$ 以下で最大の価値を返す

コード

void dfs(const int n, const long long W, const long long value, const long long weight, long long& opt,
        const int index, const vector<long long>& rsumw, const vector<int>& rminw, const vector<pair<int, int> >& vec)
{
    if(index == n){
        opt = max(opt, value);
        return;
    }
    if(rsumw[index] <= W - weight){
        long long tvalue = value;
        for(int i = index; i < n; ++i){
            tvalue += vec[i].first;
        }
        opt = max(opt, tvalue);
        return;
    }
    if(rminw[index] > W - weight) return;
    //使う
    if(weight + vec[index].second <= W){
        opt = max(opt, value + vec[index].first);
        long long tweight = weight + vec[index].second, tvalue = value + vec[index].first;
        for(int i = index + 1; i < n; ++i){
            if(tweight + vec[i].second <= W){
                tweight += vec[i].second, tvalue += vec[i].first;
            }else{
                tvalue += vec[i].first * (W - tweight) / vec[i].second;
                break;
            }
        }
        if(opt < tvalue){
            dfs(n, W, value + vec[index].first, weight + vec[index].second, opt, index + 1, rsumw, rminw, vec);
        }
    }
    //使わない
    long long tweight = weight, tvalue = value;
    for(int i = index + 1; i < n; ++i){
        if(tweight + vec[i].second <= W){
            tweight += vec[i].second, tvalue += vec[i].first;
        }else{
            tvalue += vec[i].first * (W - tweight) / vec[i].second;
            break;
        }
    }
    if(opt < tvalue){
        dfs(n, W, value, weight, opt, index + 1, rsumw, rminw, vec);
    }
}

//vec には (価値, 重さ) の順で詰める
long long solve(const int n, const long long W, vector<pair<int, int> >& vec)
{
    sort(vec.begin(), vec.end(), [&](const pair<int, int>& a, const pair<int, int>& b){
        return (long long)a.first * b.second > (long long)a.second * b.first;
    });
    vector<long long> rsumw(n + 1, 0);
    vector<int> rminw(n + 1, numeric_limits<int>::max());
    for(int i = n - 1; i >= 0; --i){
        rsumw[i] = rsumw[i + 1] + vec[i].second, rminw[i] = min(rminw[i + 1], vec[i].second);
    }
    long long opt = 0;
    dfs(n, W, 0, 0, opt, 0, rsumw, rminw, vec);
    return opt;
}

verify 用の問題

Atcoder : Sweet Alchemy 提出コード
yukicoder : Randomized 01 Knapsack 提出コード