ABC128 C Switches
出れなかったんだけど、AtCoderの解説動画が結構参考になったので、pythonで書く
数え上げ系すごく苦手なので、戒め兼補足。解説動画が少しわかりにくかったので。
問題概略。 ON/OFFスイッチが N 個と電球が M 個ある。それぞれのスイッチは複数の電球とランダムに接続されている。電球は、繋がっているスイッチのうちONになっているものの数が、偶奇どちらかの時(ランダム)、点灯する。それを決めるのは、P_i (1 ≦ i ≦ M)。全ての電球をつけるスイッチのパターンの数を求めよ。
解説では、bit全探索だった。N ≦ 10 なので、2 ^10 ≒ 10 ^3 なので十分。
まず、入力を表に接続表(隣接行列みたいなやつ)にする.
スイッチ番号\電球番号 | 0 | 1 | 2 |
---|---|---|---|
0 | 1 | 1 | 0 |
1 | 1 | 0 | 1 |
こんな感じにしたい。bit全探索にすると、実装が楽になるので、2 進数にする。この時上の表と 桁が反転することになる。
スイッチ番号\電球番号 | 2 1 0 |
---|---|
0 | 011 |
1 | 101 |
ここで、スイッチ 0 を押すと、それぞれの電球の押されているスイッチの数は
電球番号 | 2 | 1 | 0 |
---|---|---|---|
押されているスイッチの数 | 0 | 1 | 1 |
になる。 さらに、スイッチ 1 を押すと
電球番号 | 2 | 1 | 0 |
---|---|---|---|
押されているスイッチの数 | 1 | 1 | 2 |
こうなる。電球とスイッチが繋がっていない場合は、数は上がらず、スイッチを押すと、押したスイッチの接続表の行を足していく動作になる。
ここで、単純に押されているスイッチの数をそれぞれ 2 で割れば、あまりが出て、点灯しているかいないかがわかるが、単純に行を足すのではなく、排他的論理和をとれば、直接それぞれのあまりが出せて、実装が楽になる。
全探索の部分は、シフトを活かして、列挙する。1 が立っている桁と、スイッチのONが対応している具合に。
通った解答
def main(): n, m = [int(i) for i in input().split()] # a[i] には i 番目のスイッチの 電球との接続情報が入っている a = [0 for i in range(n)] for j in range(m): k, *switch = [int(i) for i in input().split()] for s in switch: a[s-1] |= (1 << j) p = int(("".join(reversed([i for i in input().split()]))), base=2) ans = 0 # bit全探索 for i in range(1<<n): t = 0 for j in range(n): if (i >> j & 1) == 1: t ^= a[j] if t == p: ans += 1 print(ans) if __name__ == '__main__': main()