Zenn
🎃

[Python] ABC399 C Make it Forest

2025/03/30に公開

[Python] ABC399 C Make it Forest

目的

  • コンテスト中に私自身が回答できなかった問題について、振り返り、理解を深めるための記事です。
  • 初心者のため誤解している部分も多いかと思いますが、理解不足の点はその旨記載しています。

問題

https://atcoder.jp/contests/abc399/tasks/abc399_c

自分の回答

  • 提出できる回答を作れず。
  • 検索した結果、DFSまたはUnion-Findを利用すれば良さそうなところまでは分かったが、取り除く辺を特定する方法を理解できずギブアップ

どうすればよかったのか

そもそも「K 個の連結成分C1C_1,C2C_2,...,CKC_K」とは…?

  • 連結成分とは、「部分グラフのうち、極大で連結なもの」らしい。
    • 簡単にいうと、ある頂点から見たときに繋がっている範囲の一塊
    • 特定の頂点と頂点を繋ぐ辺が全くなければ、別のCということ?
  • 入力1を例にすると、頂点1から見た時、Cと言えるのは以下の範囲のはず
    • 頂点1から見て、以下の形で繋がった、ひとつのC

    • 頂点2,3,4から見ても、同じ形で、頂点1から見た時と同じC

  • 入力1の場合、4つの頂点に4つの辺を持つ1個の連結成分C1C_1が存在するが、どれか1つの辺を削除すれば閉路がなくなり、今回の条件を満たす
    • これはそういうものとして覚えておくのが良さそう?
    • 実際、頂点の数より辺が少なければ閉路ができないのは理解できるので、今後活用できるようにしたい。
  • 入力1の場合、M - (N - K) に当てはめると 4 - (4 - 1) で答えの1が導き出せる

Union-Find(素集合データ構造)の使い方について

  • (参考) Union find(素集合データ構造)

  • (まっさらなところから)各頂点に対して、辺を追加していく。

    • 入力1を例にすると、最初に存在するのは、1,2,3,4 の4つのグループ
    • 1と2を繋ぐ辺を追加
      • 1&2,3,4 の3グループ
    • 1と3を繋ぐ辺を追加
      • 1&2&3,4 の2グループ
    • 2と4を繋ぐ辺を追加
      • 1&2&3&4 の1グループ
      • (2と1がすでに同じグループ、かつ2の親は1であるため、4も1を親としたグループに入る)
    • 3と4を繋ぐ辺を追加
      • すでに3と4は(1が親の)同じグループに所属しており、追加してしまうと閉路ができてしまうため、削除したい (★今回の問題ではこの数を求めたい)
      • イメージとしては以下のような感じだと思う
  • 参考ページの「Union-Find木の実装(ランク無し)」をもとに実装してみる

n, m = map(int, input().split())
uv = [list(map(int, input().split())) for i in range(m)]


class UnionFind:
    def __init__(self, n):
        # 最初は全て独立している
        self.par = list(range(n))

    def root(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.root(self.par[x])
            return self.par[x]

    def same(self, x, y):
        return self.root(x) == self.root(y)

    def unite(self, x, y):
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return
        self.par[x] = y


ans = 0
union = UnionFind(n)
for i in range(m):
    u = uv[i][0] - 1
    v = uv[i][1] - 1

    if union.same(u, v):
        ans += 1
    else:
        union.unite(u, v)

print(ans)

https://atcoder.jp/contests/abc399/submissions/64349623

補足

from atcoder.dsu import DSU

n, m = map(int, input().split())
uv = [list(map(int, input().split())) for i in range(m)]

ans = 0
union = DSU(n)
for i in range(m):
    u = uv[i][0] - 1
    v = uv[i][1] - 1

    if union.same(u, v):
        ans += 1
    else:
        union.merge(u, v)

print(ans)

https://atcoder.jp/contests/abc399/submissions/64349856

Discussion

ログインするとコメントできます