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";
}

投稿日時:
最終更新: