H - 加算と乗算 / Addition and Multiplication 解説
by
seekworser
\(N\) 個の整数の並べ方、およびそれらの間に \(+, \times\) のいずれかを全探索します。
\(N\) 個の整数の並べ方は \(N!\) 通りあり、それぞれについて各数字の間に \(+, \times\) のいずれかをいれる方法の数は \(2^{N-1}\) 通りあります。それぞれについて、\(O(N)\) 回の計算で数式の値を評価し、値が \(S\) と一致するか判定すればよいため、全体での計算量は \(O(N2^{N-1}N!)\) となります。この値は \(N=6\) で約 \(1.4 \times 10^5\) となり、実行時間制限に十分間に合います。
実装上は、
- 並べ替えの全探索は順列全探索を用いる(例えば、C++に置いては
std::next_permutation
などが利用可能です) - \(+, \times\) の入れ方の全探索はビット全探索を用いる。
- 数式の値を評価する際は、先に掛け算の結果を配列に保存し、その後配列の要素の総和を計算する。
などの方針が考えられます。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
int n; ll s;
cin >> n >> s;
vector<ll> a(n);
for (int i=0; i<n; i++) cin >> a[i];
sort(a.begin(), a.end());
while (true) {
for (ll bit=0; bit < (1LL << (n-1)); bit++) {
vector<ll> muls = {a[0]};
for (int i=1; i<n; i++) {
if ((bit >> (i-1)) & 1) muls[muls.size()-1] *= a[i];
else muls.push_back(a[i]);
}
ll total = 0;
for (auto x : muls) total += x;
if (total == s) {
string ans = to_string(a[0]);
for (int i=1; i<n; i++) {
if ((bit >> (i-1)) & 1) ans += "x";
else ans += "+";
ans += to_string(a[i]);
}
cout << "Yes\n";
cout << ans << '\n';
return 0;
}
}
if (!next_permutation(a.begin(), a.end())) break;
}
cout << "No\n";
}
投稿日時:
最終更新: