Official

F - Popcorn Editorial by yuto1115

解説

各ポップコーン売り場について訪れるか訪れないか決めることを考えます。訪れるポップコーン売り場の選び方は \(2^N\) 通りであり、本問題の制約下ではこれは最大で \(10^3\) 通りなので、容易に全通り試すことができます。具体的には、訪れるポップコーン売り場の選び方それぞれに対して、その選び方で全ての味のポップコーンを買えるか判定し、買えるならば選んだ売り場の数を \(x\) として \(\mathrm{ans} \leftarrow \min(\mathrm{ans},x)\) と更新する、という処理を行えば良いです。

あとは実装の問題です。本問題のように「\(N\) 個のものそれぞれについて選ぶか選ばないかを決める \(2^N\) 通りを全探索する」という状況では、bit 全探索と呼ばれる手法がその実装の容易さから広く用いられています。これは、「\(0,1,\dots,N-1\) の番号がついた \(N\) 個のものから \(x_1,x_2\dots,x_k\)\(k\) 個を選ぶ」という状態を「\(2\) 進数表記で \(x_1,x_2,\dots,x_k\) 桁目が \(1\) であるような整数」に対応させると、\(N\) 個のものからいくつかのものを選ぶ \(2^N\) 通りが \(0\) 以上 \(2^N-1\) 以下の整数と一対一に対応するので、選び方を全探索する代わりに整数の方を全探索すればよい(そしてこれは簡単な for 文で実現できる)というものです。

整数から選び方を復元するには「整数 \(x,k\) に対して、\(x\)\(2\) 進数表記したときの \(k\) 桁目を求める」という操作が必要になりますが、これはほとんどのプログラミング言語の備わるビット演算の機能を用いて簡単に実装できます。詳細は下記の実装例も参考にしてください。

実装例 (C++) :

#include <bits/stdc++.h>

using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<string> s(n);
    for (int i = 0; i < n; i++) {
        cin >> s[i];
    }
    int ans = n;
    for (int bit = 0; bit < (1 << n); bit++) {
        vector<bool> exist(m);
        int cnt = 0;
        for (int i = 0; i < n; i++) {
            if (bit >> i & 1) {
                ++cnt;
                for (int j = 0; j < m; j++) {
                    if (s[i][j] == 'o') exist[j] = true;
                }
            }
        }
        bool all_exist = true;
        for (int j = 0; j < m; j++) {
            if (!exist[j]) all_exist = false;
        }
        if (all_exist) ans = min(ans, cnt);
    }
    cout << ans << endl;
}

posted:
last update: