ABC282の振り返り(A, B, C, D, E)
振り返りです。
A - Generalized ABC
問題略
解説通り。
from string import ascii_uppercase def A(K): print(ascii_uppercase[:K]) A(3) A(1)結果:#37323560
自由欄
B - Let's Get a Perfect Score
問題略
やってみよう!
B
関数を完成させて「Run」ボタンをクリックしよう!
自由欄
try
ちょっと手直ししてこんな感じ。
これも解説通りです。
結果:#37630389
行列積
行列積を用いた解法も紹介されているのでやってみます。
単純に行列積を取ると「二人とも正解か」を意味する行列になってしまうので、ド・モルガンの法則の雰囲気で以下の操作をしています。
$ \overline{\overline{A} \bigcap \overline{B}} \:=\: \overline{\overline{A}} \bigcup \overline{\overline{B}} \:=\: A \bigcup B $
これは、考えつかないな…
import numpy as np def B(N, M, S): s = np.array([list(s) for s in S]) == "o" return np.triu(~(~s @ (~s).T), 1).sum() test_all(B)結果:#37631270
自由欄
C - String Delimiter
略
結果:#37334723
自由欄
D - Make Bipartite 2
やってみよう!
D
関数を完成させて「Run」ボタンをクリックしよう!
自由欄
try(コンテスト中)
そもそも二部グラフが初耳だったので、簡単な判定方法ないかなぁ~とググったら頂点倍加union-findという方法にたどり着きました。
- 二部グラフ判定をUnionFindTreeで行う - noshi91のメモ
-
@n_vip 頂点aをa0とa1に増やして、辺a-bに対してa0-b1とa1-b0を繋げば最後に全部の頂点についてx0-x1が繋がってなければ二部グラフ
— よすぽ (@yosupot) June 24, 2015
また、前処理として連結成分に分けたほうがよさそうだなぁってとこまではたどり着いたのですが、結局全探索の思考から抜け出せずにTLEのままフィニッシュでした…
最終的に以下。
from collections import defaultdict from copy import deepcopy class DSU(): def __init__(self): self.dsu = defaultdict(lambda: -1) def leader(self, i): if self.dsu[i] < 0: return i self.dsu[i] = self.leader(self.dsu[i]) return self.dsu[i] def merge(self, i, j): '''サイズの大きい方(x)に小さい方(y)をマージ''' if i == j: return x = self.leader(i) y = self.leader(j) if x == y: return if self.dsu[x] > self.dsu[y]: x, y = y, x self.dsu[x] += self.dsu[y] self.dsu[y] = x def members(self, i): l = self.leader(i) return [k for k, v in self.dsu.items() if self.leader(k) == l] def D(N, M, uv): dsu = DSU() g = DSU() edge = defaultdict(list) for u, v in uv: dsu.merge(u, N + v) dsu.merge(N + u, v) g.merge(u, v) edge[u].append(v) edge[v].append(u) leaders = dict() for i in range(1, N + 1): l = g.leader(i) if l in leaders: continue for n in g.members(l): if dsu.leader(n) == dsu.leader(n + N): leaders[l] = False break else: leaders[l] = True ans = 0 for i in range(1, N + 1): e = edge[i] for j in range(i + 1, N + 1): if j in e: continue li = g.leader(i) lj = g.leader(j) if li != lj: if leaders[li] and leaders[lj]: ans += 1 continue else: continue if not leaders[li]: continue target = deepcopy(dsu) target.merge(i, N + j) target.merge(N + i, j) for k in [i, j]: if target.leader(k) == target.leader(k + N): break else: ans += 1 return ans test_all(D)結果:#37357012
根本的な発想が誤っていたらどんなに細かくチューニングしても無駄だという典型例です…
解説を見る
という訳で解説を見ます。
結局、以下のような考え方にたどり着かなければならなかったということですね…
実装例を見ても、頂点倍加Union-Findとか小手先のテクニックは不要で単純に全探索すればよかったんや…
以下ではBFSで実装し直しました。
結果:#37672320
自由欄
E - Choose Two and Eat One
やってみよう!
E
関数を完成させて「Run」ボタンをクリックしよう!
自由欄
解説を見る
せっかくなのでE問題もやってみます。無理です。解説を見ます。
キーワードは最大全域木、Prim法、Kruskal法といったところでしょうか。
何もわからないのでググるといつものけんちょんさんです。
AtCoder ABC 282 E - Choose Two and Eat One (青色, 500 点) - けんちょんの競プロ精進記録
こんな概念があるんだなぁ(小並)。
とりあえずPythonのpow
は繰り返し二乗法で実装されてるっぽい(&modも与えられる)ので前処理は楽できます。
あとはけんちょんさんのコードをパクってこんな感じ。
結果:#37675523
自由欄