資源配分問題などとも呼ばれる、貪欲法が使える問題!
過去によく似た類題を出題したことがある。
問題概要
正の整数からなる長さ の数列
がある。頂点
をもつ木をすべて考えたときの、
の最小値を求めよ ( は頂点
の次数)。
制約
考えたこと
まず、次のことが自明に成り立つ。
ここでよく知られたこととして、逆にこれを満たす に対して、次数がこの値になるような木が存在する。このことは、プルーファーコードを用いたり、葉が必ず存在することから帰納法を活用したりすることで、証明できる。
よって問題は次の最適化問題を解くことに帰着された。
Maximize:
Subject to:
は整数
これは資源配分問題と呼ばれるものになっている。 個という有限個の資源を用いて、凸関数の和を最小化する資源配分問題だ。このような問題では、
から出発して、「 の値を 1 上昇させたときの目的関数の増分が最小であるような
に対して、[tex: x_{i} の値を 1 上昇させる」ことを繰り返す Greedy 解法が最適解を導く。
たとえば priority_queue で実装できて、その場合の計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; using pll = pair<long long, long long>; int main() { long long res = 0; int N; cin >> N; vector<long long> A(N), x(N, 1); priority_queue<pll, vector<pll>, greater<pll>> que; for (int i = 0; i < N; ++i) { cin >> A[i], res += A[i]; que.push(pll(A[i] * 3, i)); } for (int iter = 0; iter < N - 2; ++iter) { auto [add, id] = que.top(); que.pop(); ++x[id]; res += add; que.push(pll(A[id] * (x[id] * 2 + 1), id)); } cout << res << endl; }