AtCoder

ABC356 C問題(Keys)を解く

AtCoder_ABC356_C

AtCoder が提供しているABC(AtCoder Beginner Contest)356 C問題をC++とPythonで解いてみました。ABC356は、2024年6月1日21:00に実施されました。

AtCoder の紹介はこちらに、プログラミングの方針はこちらに記事があります。

C問題 Keys(Difficulty : 568)

問題の詳細は、リンク先をご覧ください。

ABC356 C問題 Keys

bit全探索を用いて、すべての鍵の組み合わせを試します。AtCoder Problems による Difficulty は 568 でした。

解答案

鍵の数 $N$ は、最大で15本です。$N$ が最大でも、$2^{15}=32768$ 通りであるため、すべての組み合わせを調べることができます。bit全探索と呼ばれる方法です。タグ「bit全探索」でこの手法を使う問題を紹介しています。

以下は、手法の特徴が分かりやすい類題です。

この問題では、変数 bit の下から $i$ bit目が1であれば、その鍵を正しい鍵とします。0であれば、ダミーの鍵です。

この変数 bit に対して、$M$ 個の条件が矛盾しなければ、正しい組み合わせとなります。矛盾するとは、以下の条件が成立することです。

  • K 本以上の正しい鍵が選ばれているのに、ドアが開かない。
  • K 本未満の正しい鍵しか選ばれていないのに、ドアが開いた。

C++ プログラム例(ABC356C)

上で説明したbit全探索を実装します。

入力は1行に複数の情報が含まれています。$i$ 番目のテストで使う鍵の個数 c を読み、その鍵の組 a[i] と、結果 r[i] を格納します。

変数 bit は、0 から $2^N – 1$ を正しい鍵の組み合わせとして調べます。

  • それぞれのテストケースで該当する鍵の数 tk を求めます。
  • 上記で考察した2つの条件に矛盾しないか判定します。
  • すべてのテストケースで矛盾しなければ、解答となる変数 result を増やします。

以下が、C++プログラムです。bit全探索のポイントになる行の背景色を変更しました。

#include <bits/stdc++.h>
using namespace std;

typedef unsigned int uint;

int main()
{
	int n, m, k;
	cin >> n >> m >> k;
	vector<vector<int>> a(m);
	vector<bool> r(m);
	for (int i = 0; i < m; ++i) {
		int c;
		cin >> c;
		a[i].assign(c, 0);
		for (int j = 0; j < c; ++j) {
			cin >> a[i][j];
			--a[i][j];	
		}
		char ch;
		cin >> ch;
		if (ch == 'o') {
			r[i] = true;
		} else {
			r[i] = false;
		}
	}

	int result = 0;
	for (uint bit = 0; bit < (1 << n); ++bit) {
		bool t = true;
		for (int i = 0; i < m; ++i) {
			int tk = 0;
			for (int j = 0; j < a[i].size(); ++j) {
				if ((bit & (1 << a[i][j])) != 0) {
					++tk;
				}
			}
			if (((tk >= k) && !r[i])) {
				t = false;
			}
			if (((tk < k) && r[i])) {
				t = false;
			}
		}
		if (t) {
			++result;
		}
	}

	cout << result << endl;

	return 0;
}

AC(Accepted=正しいプログラム)と判定されました。

Python プログラム例(ABC356C)

Python版も基本的な考え方は同じです。以下がプログラムです。

"""AtCoder Beginner Contest 356 C"""
n, m, k = map(int, input().split())
a = [[] for _ in range(m)]
r = [False] * m
for i in range(m):
    c_a_r = input().split()
    c = int(c_a_r[0])
    for j in range(c):
        a[i].append(int(c_a_r[1 + j]) - 1)
    if c_a_r[-1] == 'o':
        r[i] = True
    else:
        r[i] = False

result = 0
for bit in range(1 << n):
    t = True
    for i in range(m):
        tk = 0
        for j in range(len(a[i])):
            if (bit & (1 << a[i][j])) != 0:
                tk += 1
        if tk >= k and not r[i]:
            t = False
        if tk < k and r[i]:
            t = False
    if t:
        result += 1

print(result)

こちらも「AC」と判定されました。

最後に

bit全探索という定番の考え方を用いて、問題を解くことができました。

引き続き ABC の問題を紹介していきます。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA