この辺りから難しくなって来てる。「 で合格・不合格」という条件をどのように適切に言い換えるかが問われている。
問題概要
電源が 個、モーターが 個、ケーブルが 個ある。電源は と番号づけられていて、モーターは と番号づけられていて、ケーブルは と番号づけらている。
電源とモーターとケーブルを 1 個ずつ組み合わせて製品ができる。3 つの部品がすべて正常だと、製品は「合格」となる。3 つの部品のうち 1 つ以上が故障していると、製品は「不合格」となる。
今、 個の情報が与えられる。各情報は「電源 とモーター とケーブル を組み合わせてできる製品が合格であるか不合格しているか」を表す。
この情報から、各部品について
- 0:確実に故障していると言える
- 1:確実に正常であると言える
- 2:故障しているか正常であるかを言い切れない
を答えよ。
制約
Step 1:合格製品から考える
この手の問題で問われていることは、今後より高度な問題を解くときにも必要になるイメージ。なので教育的問題と言えそう。
まず、「製品が合格」という情報のみを抽出して処理していくことにする。部品 からできる製品が正常ならば、 は確実に正常であるとしてよい。
こうして、確実に正常である部品はすべて洗い出すことができる。この段階で「確実に正常」とはならなかった部品は、今後「確実に正常」となることはない。
Step 2:不合格製品を考える
続いて、不合格製品を考えていこう。この場合は、合格製品ほど強い情報は得られない。部品 からなる製品が不合格だとしても、 のうち 1 個以上が故障していることしかわからない。
しかし、 のうちの 2 個が正常であれば、残りの 1 個が異常であると分かる。逆に、それ以外の場合で「確実に異常」を特定できることはあり得ない。たとえば、
- の 3 つが「確実に正常」となることは問題の制約からあり得ない
- が「確実に正常」で、 が「確実に異常」だとすでにわかっているとき: が正常か異常かはわからない
- がともに「正常か異常か分からない」場合、 は正常か異常かはわからない
などというふうになっている。他の場合も同様に確認できる。
また、Step 1 で、確実に正常と言える部品はすべて洗い出されているため、考慮する不合格製品の順序はなんでもよいし、不合格製品たちを完全に独立に考えて大丈夫。
以上の解法の計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; int main() { // 入力 int A, B, C, N; cin >> A >> B >> C >> N; vector<vector<int>> ok, ng; for (int i = 0; i < N; ++i) { int a, b, c, isok; cin >> a >> b >> c >> isok; --a, --b, --c; if (isok) ok.push_back(vector<int>({a, b, c})); else ng.push_back(vector<int>({a, b, c})); } // デフォルトでは「分からない」としておく vector<int> res(A + B + C, 2); // 検査結果「合格」の情報から、確実に OK な部分を先に埋める for (auto v : ok) { int a = v[0], b = v[1], c = v[2]; res[a] = res[b] = res[c] = 1; } // 検査結果「不合格」の情報から、確実に NG な部分を埋めていく (3 つ中 2 つが OK) for (auto v : ng) { int a = v[0], b = v[1], c = v[2]; if (res[b] == 1 && res[c] == 1) res[a] = 0; else if (res[c] == 1 && res[a] == 1) res[b] = 0; else if (res[a] == 1 && res[b] == 1) res[c] = 0; } // 出力 for (auto v : res) cout << v << endl; }