解法.枝刈り探索
枝刈り探索を用いたヒューリスティックな解法を説明します。
初めに、 の素因数分解を として求めます。今回の問題は複数のテストケースが一度に与えられるので、初めに までの素数をエラトステネスの篩で求めて、各テストケースに対してその素数表を用いた素因数分解(自明な分割)を行います。何度も素因数分解を行う場合は予め素数表を求めることによって計算時間が高速になります。
制約 より に対して
・
・
・
・ かつ
となる を全探索します。
探索する順番は とします。ただし、 と の値が求まっているときには から も定まります。
高速化として かつ を満たすように探索をして、探索途中の値 が暫定値よりも悪くなるなら枝刈りをして探索を止めます。
計算時間は分かりませんが、素因数の個数 より
、
となるので数分以内で解けるのではと予想できます。
計算時間: 不明(解析を追記したので下を参考に)
#include <iostream>
#include <vector>
#include <tuple>
#include <numeric>
#include <cmath>
using namespace std;
using ll = long long;
template<class T>
std::vector<T> GetPrimes(const T N) {
std::vector<bool> is_p(N + 1, true);
std::vector<T> primes;
is_p[0] = is_p[1] = false;
for (T i = 2; i <= N; ++i) {
if (is_p[i]) {
primes.push_back(i);
for (T j = i * i; j <= N; j += i)
is_p[j] = false;
}
}
return primes;
}
template<class T>
struct PrimeFactorization {
PrimeFactorization() {
primes = GetPrimes(MAX_N);
}
const ll MAX_N = 32'000'000;
std::vector<T> primes;
std::vector<std::pair<T, T>> GetPrimeFactorization(T n) {
std::vector<std::pair<T, T>> pf;
T m = n;
for (const T p : primes) {
if (p * p > n) break;
if (m % p != 0) continue;
T cnt = 0;
while (m % p == 0) { ++cnt; m /= p; }
pf.emplace_back(std::make_pair(p, cnt));
}
if (1 < m) pf.emplace_back(std::make_pair(m, 1));
return pf;
}
};
ll ans;
void Solve(const ll a, const int idx_a, const ll b, const int idx_b, const ll c, vector<pair<ll, ll>>& pf) {
const int size = pf.size();
if (idx_a >= size && idx_b >= size) {
ans = std::min(ans, a + b + c);
return ;
}
auto& pf_idx = (idx_a < size) ? pf[idx_a] : pf[idx_b];
const ll ub = pf_idx.second;
for (ll p = 0, mul = 1; p <= ub; ++p) {
pf_idx.second = ub - p;
if (idx_a < size) {
Solve(mul * a, idx_a + 1, b, idx_b, c, pf);
}
else {
const ll mul_c = std::pow(pf_idx.first, pf_idx.second);
if (a >= mul * b && a >= mul_c * c && a + mul * b + mul_c * c < ans)
Solve(a, idx_a, mul * b, idx_b + 1, mul_c * c, pf);
}
mul *= pf_idx.first;
}
pf_idx.second = ub;
}
int main() {
PrimeFactorization<ll> pf;
ll n;
while (cin >> n, n) {
auto pf_n = pf.GetPrimeFactorization(n);
ans = std::numeric_limits<ll>::max();
Solve(1, 0, 1, 0, 1, pf_n);
std::cout << ans << std::endl;
}
return 0;
}
ICPC ではコンテスト中に正解できればいいので、今回のような計算時間が分からないけど数十分かければ通るアルゴリズムを選択するのも戦略としてはありだと思います。