最小全域木とは簡単に説明するとグラフのすべての頂点は最も少ないコストの辺で構成された木のことです。
使用しているアルゴリズムはクラスカル法です。
これも簡単に言うとグラフから最もコストの少ない辺をループができないように追加するという処理を繰り返してすべての頂点を選択したところで終了します。
Union Find
クラスカル法を実装するにあたってどの頂点がどの木に所属しているかという判定が必要になります。
同じ木に属している場合ループができるのでこれを防ぐ必要があります。
この時、効率よく判定するデータ構造としてUnion Find木というのが使われます。
簡単にいうと木の根(ルート)が同じだと同じ木だと判断するという考えです。
class UnionTree
{
List<int> par;
public UnionTree(int n)
{
par = new List<int>(n);
for (int i=0;i<n;i++)
{
par.Add(i);
}
}
public int root(int x)
{
if (par[x] == x)
{
return x;
}
else
{
return par[x] = root(par[x]);
}
}
public bool same(int x, int y)
{
return root(x) == root(y);
}
public void unite(int x, int y)
{
x = root(x);
y = root(y);
if (x == y) return;
par[x] = y;
}
}
上記のコードのList<int> par
はiの親ノードを表していてます。言い換えるとi番目のノードの親はpar[i]ということです。
same()
は親ノードを辿って同じルートに行き着くなら同じ木という判断をします。
unite
は同じ根を持つ木をつなげます。
辺の表現
IComparable
を実装することによって比較できるようにします。
class Edge : IComparable
{
public int u, v, cost;
public Edge(int u, int v, int cost)
{
this.u = u;
this.v = v;
this.cost = cost;
}
public int CompareTo(object obj)
{
return cost.CompareTo((obj as Edge).cost);
}
}
クラスカル法の実装
edge.Sort()
することによって辺を重みごとに並べます。
あとは辺を重みが低い順に追加していきますが、頂点が同じ木に属している時は追加しません。
public static int kruskar(List<Edge> edge,int n)
{
edge.Sort();
var union = new UnionTree (n);
int w = 0;
for (int i=0; i<n; i++)
{
Edge e = edge[i];
if (!union.same(e.u, e.v)) {
union.unite(e.u, e.v);
w += e.cost;
}
}
return w;
}
コードの全景です。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SpanningTree
{
class UnionTree
{
List<int> par;
public UnionTree(int n)
{
par = new List<int>(n);
for (int i=0;i<n;i++)
{
par.Add(i);
}
}
public int root(int x)
{
if (par[x] == x)
{
return x;
}
else
{
return par[x] = root(par[x]);
}
}
public bool same(int x, int y)
{
return root(x) == root(y);
}
public void unite(int x, int y)
{
x = root(x);
y = root(y);
if (x == y) return;
par[x] = y;
}
}
class Edge : IComparable
{
public int u, v, cost;
public Edge(int u, int v, int cost)
{
this.u = u;
this.v = v;
this.cost = cost;
}
public int CompareTo(object obj)
{
return cost.CompareTo((obj as Edge).cost);
}
}
class Program
{
public static int kruskar(List<Edge> edge,int n)
{
edge.Sort();
var union = new UnionTree (n);
int w = 0;
for (int i=0; i<n; i++)
{
Edge e = edge[i];
if (!union.same(e.u, e.v)) {
union.unite(e.u, e.v);
w += e.cost;
}
}
return w;
}
static void Main(string[] args)
{
string line = Console.ReadLine();
var vals = line.Split(' ');
int E = Int32.Parse(vals[1]);
List<Edge> edge = new List<Edge>();
for (int i=0;i<E;i++)
{
line = Console.ReadLine();
vals = line.Split(' ');
int s = Int32.Parse(vals[0]);
int t = Int32.Parse(vals[1]);
int w = Int32.Parse(vals[2]);
Edge e = new Edge(s,t,w);
edge.Add(e);
}
int c = kruskar(edge, E);
Console.WriteLine(c);
}
}
}