AtCoder Beginner Contest 015のC問題 「高橋くんのバグ探し」

C - 高橋くんのバグ探し

入力:
 N\ K
 \displaystyle{T}_{{{1},{1}}}\ {T}_{{{1},{2}}}\ \ldots{T}_{{{1},{K}}}
 \displaystyle{T}_{{{2},{1}}}\ {T}_{{{2},{2}}}\ \ldots{T}_{{{2},{K}}}
 \displaystyle\vdots
 \displaystyle{T}_{{{N},{1}}}\ {T}_{{{N},{2}}}\ \ldots{T}_{{{N},{K}}}
出力: 条件を満たすなら 'Found' 、そうでなければ 'Nothing'
制約:
 1 \le N \le 5
 1 \le K \le 5
 \displaystyle{0}\le{T}_{{{i},{j}}}\le{127}

入力の下  N 行のそれぞれから、整数を一つずつ選びます。選ばれた整数全ての排他的論理和(XOR)の値が  0 になるような組があったら 'Found' 、そうでなければ 'Nothing' を出力します。

この問題で難しいのは、各行から整数を選んでいくところです。簡単に考えられる方法としては、  N 重ループを書くことで全探索することが挙げられますが、  N = 1, 2, 3, 4, 5 それぞれの場合について実装するのは大変です(仮に  N = 100 のときも動くプログラムのことを考えると、私は正気を保ちつつコードを書ける自信がありません)。このような場合、再帰関数を実装するのが正攻法です。
ここでは、「配列を受け取り、その長さが  N より小さいときは配列に \displaystyle{0},\ {1},\ \ldots,\ {K}-{1} のいずれかを追加する。そうでないときは、その配列を用いて排他的論理和の値の判定を行い、 XOR が 0 になる組があれば true を返す」関数を考えます。
一つの関数を実装することで、  K^N 通りの整数の組を検証できているところがポイントです。

なお、D言語では arr ~ elem で「配列 arr の末尾に要素 elem を追加した配列」を得ることができます。
また、 foreach(i, v; arr) では i に添字、 v に arr[i] が代入されています。

void main() {
	/* 入力 */
	auto ip = readAs!(int[]), N = ip[0], K = ip[1];
	auto T = readMatrix!int(N, K);

	bool check(int[] arr) {
		if(arr.length < N) {
			foreach(i; 0..K) {
				if(check(arr ~ i)) return true;
			}
			return false;
		}
		int tmp = 0;
		foreach(i, v; arr) {
			tmp ^= T[i][v];
		}
		return tmp == 0;
	}

	if(check([])) writeln("Found");
	else writeln("Nothing");
}

一つでもXORが 0 になる組がある場合は 'Found' になることに注意して実装します。
一文にまとめると「再帰関数を書いて深さ優先探索」です。

感想: AtCoder Problems の Recommendations から一問解いてみました。Difficulty は 1172 となっていますが、現在のABCで出題したらきっと茶パフォ程度になるのかな〜と思います。

解答: Submission #10405209 - AtCoder Beginner Contest 015