Codeforces Round #491 (Div.2) F. Concise and clear
解法
冪乗の形を使っていかに短くできるか,という問題.
まず,n * m (n, m はただの自然数)のような表現は完全に不利.
例えば 99 * 99 を考えてみるとわかりやすいが,そのまま出力したほうが短いからである.
すると,足し算以外で考えるべきは
- p ^ q
- a * p ^ q
- p ^ q * x ^ y
しかない.
p ^ q として使いうる候補は数えてみるとわかるが多くないので,全部作ってしまう.
p ^ q * x ^ y についてだが,それぞれの p ^ q, x ^ y の長さはたかだか4であるとして良い(つまり3文字か4文字の p ^ qだけ考えればよい).
6文字以上はオーバーするから当然いらない.問題は5文字の p ^ q をなぜ考慮しなくてよいかである.
与えられる入力がたかだか10桁であることに注目する.n = 10^10 の場合は 10 ^ 10 が当然最適なので関係ない.つまり9桁の場合のみ注目すればいい.
すると,p ^ q で5文字使ってしまうと "^" を考慮して x ^ y で使えるのはあと3文字分しかない.
しかし3文字使っていいなら,トータル9文字になるので入力をそのまま出力すればいい.したがって5文字は考えなくて良いことがわかる.
最後に,a * (上で求めた冪の形)+ b を全部ためして一番短いやつを選ぶ.
ソースコード
#include <bits/stdc++.h> using namespace std; using ll = long long; int main() { ll n; cin >> n; const ll ub = n; map<ll, string> pw, pw_len3or4; for(ll i = 2; i * i <= ub; ++i) { ll p = i; for(int j = 2; p * i <= ub; ++j) { p *= i; string s = to_string(i) + "^" + to_string(j); if(pw.count(p) == 0 || pw[p].size() > s.size()) { pw[p] = s; } } } for(auto& p : pw) { if(p.second.size() == 3 || p.second.size() == 4u) { pw_len3or4.insert(p); } } for(auto& p : pw_len3or4) { for(auto& q : pw_len3or4) { if(p.first > ub / q.first) continue; string s = p.second + "*" + q.second; ll val = p.first * q.first; if(val <= ub && (pw.count(val) == 0 || pw[val].size() > s.size())) { pw[val] = s; } } } string ans = to_string(n); for(auto& p : pw) { ll d = n / p.first; if(d == 0) break; string s; if(d > 1) s += to_string(d) + "*"; s += p.second; ll r = n % p.first; if(r > 0) { s += "+"; if(pw.count(r) && to_string(r).size() > pw[r].size()) { s += pw[r]; } else { s += to_string(r); } } if(ans.size() > s.size()) { ans = s; } } cout << ans << endl; }
感想
本番ではやばいと思ってほとんど考えず諦めて hack に移行してしまったが,もったいなかった.