using System; using System.Collections.Generic; using System.Linq; using Bang.Graphs.Spp; class T { static int[] Read() => Array.ConvertAll(Console.ReadLine().Split(), int.Parse); static void Main() { var z = Read(); var (h, w) = (z[0], z[1]); var sv = new Point(z[2], z[3]) - (1, 1); var ev = new Point(z[4], z[5]) - (1, 1); var s = GraphConsole.ReadGrid(h); var factory = ShortestPath.ForGrid(h, w); var r = Bfs(factory, v => Array.FindAll(v.Nexts(), nv => s.GetValue(nv) != '#'), sv, ev); Console.WriteLine(r); } static long Bfs(SppFactory factory, Func nextVertexesMap, TVertex startVertex, TVertex endVertex) { Func TEquals = EqualityComparer.Default.Equals; var costs = factory.CreateMap(long.MaxValue); var costs2 = factory.CreateMap(long.MaxValue); var inVertexes = factory.CreateMap(factory.Invalid); var q = new Queue(); costs[startVertex] = 0; q.Enqueue(startVertex); while (q.Count > 0) { var v = q.Dequeue(); var nc = costs[v] + 1; foreach (var nv in nextVertexesMap(v)) { if (costs[nv] <= nc) { if (TEquals(inVertexes[v], nv)) continue; costs2[nv] = Math.Min(costs2[nv], nc); } else { costs[nv] = nc; inVertexes[nv] = v; if (TEquals(nv, endVertex)) continue; q.Enqueue(nv); } } } if (costs[endVertex] == long.MaxValue) return -1; var path = new Stack(); for (var v = endVertex; !TEquals(v, factory.Invalid); v = inVertexes[v]) path.Push(v); var others = path.Where(v => costs2[v] != long.MaxValue).ToArray(); if (others.Length == 0) return -1; return 2 * costs[endVertex] + others.Min(v => costs2[v] - costs[v]); } } namespace Bang.Graphs.Spp { /// /// 頂点を表すデータの種類に応じて、 オブジェクトを取得します。 /// public static class ShortestPath { /// /// 頂点が整数値で表されるグラフを使用します。
/// この値の範囲は [0, ) です。 ///
/// 頂点の個数。 /// 整数値に対する Factory オブジェクト。 /// /// 有向グラフ上での典型的な Dijkstra: /// /// var r = ShortestPath.ForInt(n) /// .ForWeightedMap(es, true) /// .Dijkstra(sv, ev); /// /// public static IntSppFactory ForInt(int vertexesCount) => new IntSppFactory(vertexesCount); /// /// 頂点が 2 次元グリッド上の点で表されるグラフを使用します。 /// /// 高さ。 /// 幅。 /// 2 次元グリッド上の点に対する Factory オブジェクト。 /// /// 無向グリッド上での典型的な BFS: /// /// var r = ShortestPath.ForGrid(h, w) /// .ForUnweightedMap(v => Array.FindAll(v.Nexts(), nv => s.GetValue(nv) != '#')) /// .Bfs(sv, ev); /// /// public static GridSppFactory ForGrid(int height, int width) => new GridSppFactory(height, width); /// /// ハッシュ関数により、頂点が任意の値で表されるグラフを使用します。
/// ハッシュ値の範囲は [0, ) です。 ///
/// 頂点を表すオブジェクトの型。 /// 頂点の個数。 /// ハッシュ関数。 /// 無効な頂点を表す値。 /// ハッシュ関数を使用する場合の Factory オブジェクト。 /// /// 無向グリッド上での典型的な BFS: /// /// var r = ShortestPath.ForHash(h * w, GridHelper.CreateToHash(w), (-1, -1)) /// .ForUnweightedMap(v => Array.FindAll(v.Nexts(), nv => s.GetValue(nv) != '#')) /// .Bfs(sv, ev); /// /// public static HashSppFactory ForHash(int vertexesCount, Func toHash, TVertex invalid) => new HashSppFactory(vertexesCount, toHash, invalid); } /// /// 実装による内部データ構造の違いを吸収します。 /// /// 頂点を表すオブジェクトの型。 public abstract class SppFactory { public abstract TVertex Invalid { get; } public abstract Map CreateMap(TValue iv); public abstract ListMap CreateListMap(); /// /// 隣接頂点を動的に取得するための関数を指定します。 /// /// 指定された頂点からの行先となる頂点を取得するための関数。 /// アルゴリズムを実行するためのオブジェクト。 public UnweightedFuncMapSpp ForUnweightedMap(Func getNextVertexes) { var map = new FuncReadOnlyMap(getNextVertexes); return new UnweightedFuncMapSpp(this, map); } /// /// 隣接辺を動的に取得するための関数を指定します。 /// /// 指定された頂点からの出辺を取得するための関数。 /// アルゴリズムを実行するためのオブジェクト。 public WeightedFuncMapSpp ForWeightedMap(Func[]> getNextEdges) { var map = new FuncReadOnlyMap[]>(getNextEdges); return new WeightedFuncMapSpp(this, map); } public UnweightedListMapSpp ForUnweightedMap() { var map = CreateListMap(); return new UnweightedListMapSpp(this, map); } public WeightedListMapSpp ForWeightedMap() { var map = CreateListMap>(); return new WeightedListMapSpp(this, map); } /// /// 重みなし辺のリストを静的に指定します。 /// /// 辺のリスト。 /// 有向グラフかどうかを示す値。 /// アルゴリズムを実行するためのオブジェクト。 public UnweightedListMapSpp ForUnweightedMap(Edge[] edges, bool directed) { var map = CreateListMap(); GraphConvert.UnweightedEdgesToMap(map, edges, directed); return new UnweightedListMapSpp(this, map); } /// /// 重み付き辺のリストを静的に指定します。 /// /// 辺のリスト。 /// 有向グラフかどうかを示す値。 /// アルゴリズムを実行するためのオブジェクト。 public WeightedListMapSpp ForWeightedMap(Edge[] edges, bool directed) { var map = CreateListMap>(); GraphConvert.WeightedEdgesToMap(map, edges, directed); return new WeightedListMapSpp(this, map); } } public class IntSppFactory : SppFactory { public int VertexesCount { get; } public IntSppFactory(int vertexesCount) { VertexesCount = vertexesCount; } public override int Invalid => -1; public override Map CreateMap(TValue iv) { return new IntMap(VertexesCount, iv); } public override ListMap CreateListMap() { return new IntListMap(VertexesCount); } public UnweightedListMapSpp ForUnweightedMap(int[][] edges, bool directed) { return ForUnweightedMap(Array.ConvertAll(edges, EdgeHelper.ToEdge), directed); } public WeightedListMapSpp ForWeightedMap(int[][] edges, bool directed) { return ForWeightedMap(Array.ConvertAll(edges, EdgeHelper.ToEdge), directed); } } public class GridSppFactory : SppFactory { public int Height { get; } public int Width { get; } public GridSppFactory(int height, int width) { Height = height; Width = width; } public override Point Invalid => (-1, -1); public override Map CreateMap(TValue iv) { return new GridMap(Height, Width, iv); } public override ListMap CreateListMap() { return new GridListMap(Height, Width); } } public class HashSppFactory : SppFactory { public int VertexesCount { get; } public Func ToHash { get; } public HashSppFactory(int vertexesCount, Func toHash, TVertex invalid) { VertexesCount = vertexesCount; ToHash = toHash; Invalid = invalid; } public override TVertex Invalid { get; } public override Map CreateMap(TValue iv) { return new HashMap(VertexesCount, iv, ToHash); } public override ListMap CreateListMap() { return new HashListMap(VertexesCount, ToHash); } } public abstract class UnweightedMapSpp { static readonly Func TEquals = EqualityComparer.Default.Equals; public SppFactory Factory { get; } public abstract ReadOnlyMap NextVertexesMap { get; } protected UnweightedMapSpp(SppFactory factory) { Factory = factory; } public TVertex StartVertex { get; private set; } public TVertex EndVertex { get; private set; } public Map Costs { get; private set; } public Map InVertexes { get; private set; } public long this[TVertex vertex] => Costs[vertex]; public bool IsConnected(TVertex vertex) => Costs[vertex] != long.MaxValue; /// /// 幅優先探索により、始点から各頂点への最短経路を求めます。
/// 辺のコストはすべて 1 として扱われます。 ///
/// 始点。 /// 終点。終点を指定しない場合、Factory.Invalid。 /// 現在のオブジェクト。 /// /// グラフの有向性、連結性、多重性、開閉を問いません。 /// public UnweightedMapSpp Bfs(TVertex startVertex, TVertex endVertex) { StartVertex = startVertex; EndVertex = endVertex; Costs = Factory.CreateMap(long.MaxValue); InVertexes = Factory.CreateMap(Factory.Invalid); var q = new Queue(); Costs[startVertex] = 0; q.Enqueue(startVertex); while (q.Count > 0) { var v = q.Dequeue(); var nc = Costs[v] + 1; foreach (var nv in NextVertexesMap[v]) { if (Costs[nv] <= nc) continue; Costs[nv] = nc; InVertexes[nv] = v; if (TEquals(nv, endVertex)) return this; q.Enqueue(nv); } } return this; } public TVertex[] GetPathVertexes(TVertex endVertex) { if (InVertexes == null) throw new InvalidOperationException("No Result."); var path = new Stack(); for (var v = endVertex; !TEquals(v, Factory.Invalid); v = InVertexes[v]) path.Push(v); return path.ToArray(); } } public class UnweightedFuncMapSpp : UnweightedMapSpp { public override ReadOnlyMap NextVertexesMap { get; } public UnweightedFuncMapSpp(SppFactory factory, FuncReadOnlyMap nextVertexesMap) : base(factory) { NextVertexesMap = nextVertexesMap; } } public class UnweightedListMapSpp : UnweightedMapSpp { ListMap map; public override ReadOnlyMap NextVertexesMap => map; public UnweightedListMapSpp(SppFactory factory, ListMap nextVertexesMap) : base(factory) { map = nextVertexesMap; } public void AddEdge(Edge edge, bool directed) { map.Add(edge.From, edge.To); if (!directed) map.Add(edge.To, edge.From); } public void AddEdge(TVertex from, TVertex to, bool directed) { map.Add(from, to); if (!directed) map.Add(to, from); } } public abstract class WeightedMapSpp { static readonly Func TEquals = EqualityComparer.Default.Equals; public SppFactory Factory { get; } public abstract ReadOnlyMap[]> NextEdgesMap { get; } protected WeightedMapSpp(SppFactory factory) { Factory = factory; } public TVertex StartVertex { get; private set; } public TVertex EndVertex { get; private set; } public Map Costs { get; private set; } public Map> InEdges { get; private set; } public long this[TVertex vertex] => Costs[vertex]; public bool IsConnected(TVertex vertex) => Costs[vertex] != long.MaxValue; /// /// Dijkstra 法により、始点から各頂点への最短経路を求めます。
/// 辺のコストは非負でなければなりません。 ///
/// 始点。 /// 終点。終点を指定しない場合、Factory.Invalid。 /// 現在のオブジェクト。 /// /// グラフの有向性、連結性、多重性、開閉を問いません。 /// public WeightedMapSpp Dijkstra(TVertex startVertex, TVertex endVertex) { StartVertex = startVertex; EndVertex = endVertex; Costs = Factory.CreateMap(long.MaxValue); InEdges = Factory.CreateMap(new Edge(Factory.Invalid, Factory.Invalid, long.MinValue)); var q = PriorityQueue.CreateWithKey(v => Costs[v]); Costs[startVertex] = 0; q.Push(startVertex); while (q.Any) { var (v, c) = q.Pop(); if (TEquals(v, endVertex)) break; if (Costs[v] < c) continue; // IEnumerable, List, T[] の順に高速になります。 foreach (var e in NextEdgesMap[v]) { var (nv, nc) = (e.To, c + e.Cost); if (Costs[nv] <= nc) continue; Costs[nv] = nc; InEdges[nv] = e; q.Push(nv); } } return this; } /// /// 幅優先探索の拡張により、始点から各頂点への最短経路を求めます。
/// 例えば = 3 のとき、012-BFS を表します。
/// 辺のコストの範囲は [0, ) です。 ///
/// 辺のコストの候補となる数。 /// 始点。 /// 終点。終点を指定しない場合、Factory.Invalid。 /// 現在のオブジェクト。 /// /// グラフの有向性、連結性、多重性、開閉を問いません。 /// public WeightedMapSpp BfsMod(int m, TVertex startVertex, TVertex endVertex) { StartVertex = startVertex; EndVertex = endVertex; Costs = Factory.CreateMap(long.MaxValue); InEdges = Factory.CreateMap(new Edge(Factory.Invalid, Factory.Invalid, long.MinValue)); var qs = Array.ConvertAll(new bool[m], _ => new Queue()); Costs[startVertex] = 0; qs[0].Enqueue(startVertex); for (long c = 0; Array.Exists(qs, q => q.Count > 0); ++c) { var q = qs[c % m]; while (q.Count > 0) { var v = q.Dequeue(); if (TEquals(v, endVertex)) return this; if (Costs[v] < c) continue; foreach (var e in NextEdgesMap[v]) { var (nv, nc) = (e.To, c + e.Cost); if (Costs[nv] <= nc) continue; Costs[nv] = nc; InEdges[nv] = e; qs[nc % m].Enqueue(nv); } } } return this; } public TVertex[] GetPathVertexes(TVertex endVertex) { if (InEdges == null) throw new InvalidOperationException("No Result."); var path = new Stack(); for (var v = endVertex; !TEquals(v, Factory.Invalid); v = InEdges[v].From) path.Push(v); return path.ToArray(); } public Edge[] GetPathEdges(TVertex endVertex) { if (InEdges == null) throw new InvalidOperationException("No Result."); var path = new Stack>(); for (var e = InEdges[endVertex]; !TEquals(e.From, Factory.Invalid); e = InEdges[e.From]) path.Push(e); return path.ToArray(); } } public class WeightedFuncMapSpp : WeightedMapSpp { public override ReadOnlyMap[]> NextEdgesMap { get; } public WeightedFuncMapSpp(SppFactory factory, FuncReadOnlyMap[]> nextEdgesMap) : base(factory) { NextEdgesMap = nextEdgesMap; } } public class WeightedListMapSpp : WeightedMapSpp { ListMap> map; public override ReadOnlyMap[]> NextEdgesMap => map; public WeightedListMapSpp(SppFactory factory, ListMap> nextEdgesMap) : base(factory) { map = nextEdgesMap; } public void AddEdge(Edge edge, bool directed) { map.Add(edge.From, edge); if (!directed) map.Add(edge.To, edge.Reverse()); } public void AddEdge(TVertex from, TVertex to, long cost, bool directed) { map.Add(from, new Edge(from, to, cost)); if (!directed) map.Add(to, new Edge(to, from, cost)); } } public struct Point : IEquatable { public int i, j; public Point(int i, int j) { this.i = i; this.j = j; } public void Deconstruct(out int i, out int j) { i = this.i; j = this.j; } public override string ToString() => $"{i} {j}"; public static Point Parse(string s) => Array.ConvertAll(s.Split(), int.Parse); public static implicit operator Point(int[] v) => (v[0], v[1]); public static explicit operator int[](Point v) => new[] { v.i, v.j }; public static implicit operator Point((int i, int j) v) => new Point(v.i, v.j); public static explicit operator (int, int)(Point v) => (v.i, v.j); public bool Equals(Point other) => i == other.i && j == other.j; public static bool operator ==(Point v1, Point v2) => v1.Equals(v2); public static bool operator !=(Point v1, Point v2) => !v1.Equals(v2); public override bool Equals(object obj) => obj is Point v && Equals(v); public override int GetHashCode() => (i, j).GetHashCode(); public static Point operator -(Point v) => new Point(-v.i, -v.j); public static Point operator +(Point v1, Point v2) => new Point(v1.i + v2.i, v1.j + v2.j); public static Point operator -(Point v1, Point v2) => new Point(v1.i - v2.i, v1.j - v2.j); public bool IsInRange(int height, int width) => 0 <= i && i < height && 0 <= j && j < width; public Point[] Nexts() => new[] { new Point(i - 1, j), new Point(i + 1, j), new Point(i, j - 1), new Point(i, j + 1) }; public static Point[] NextsByDelta { get; } = new[] { new Point(-1, 0), new Point(1, 0), new Point(0, -1), new Point(0, 1) }; public int NormL1 => Math.Abs(i) + Math.Abs(j); public double Norm => Math.Sqrt(i * i + j * j); } public struct Edge { public T From { get; } public T To { get; } public long Cost { get; } public Edge(T from, T to, long cost = 1) { From = from; To = to; Cost = cost; } public override string ToString() => $"{{{From}}} {{{To}}} {Cost}"; public static implicit operator Edge((T from, T to) v) => new Edge(v.from, v.to); public static implicit operator Edge((T from, T to, long cost) v) => new Edge(v.from, v.to, v.cost); public Edge Reverse() => new Edge(To, From, Cost); } public static class EdgeHelper { public static Edge ToEdge(int[] e) => new Edge(e[0], e[1], e.Length > 2 ? e[2] : 1); public static Edge ToEdge(long[] e) => new Edge((int)e[0], (int)e[1], e.Length > 2 ? e[2] : 1); } public abstract class Map { public abstract TValue this[TKey key] { get; set; } } public abstract class ReadOnlyMap { public abstract TValue this[TKey key] { get; } } public abstract class ListMap : ReadOnlyMap { public abstract void Add(TKey key, TValue value); } public class IntMap : Map { TValue[] a; public IntMap(int count, TValue iv) { a = Array.ConvertAll(new bool[count], _ => iv); } public override TValue this[int key] { get => a[key]; set => a[key] = value; } } public class GridMap : Map { TValue[][] a; public GridMap(int height, int width, TValue iv) { a = Array.ConvertAll(new bool[height], _ => Array.ConvertAll(new bool[width], __ => iv)); } public override TValue this[Point key] { get => a[key.i][key.j]; set => a[key.i][key.j] = value; } } public class HashMap : Map { TValue[] a; Func ToHash; public HashMap(int count, TValue iv, Func toHash) { a = Array.ConvertAll(new bool[count], _ => iv); ToHash = toHash; } public override TValue this[TKey key] { get => a[ToHash(key)]; set => a[ToHash(key)] = value; } } public class FuncReadOnlyMap : ReadOnlyMap { Func GetValue; public FuncReadOnlyMap(Func getValue) { GetValue = getValue; } public override TValue this[TKey key] => GetValue(key); } public class IntListMap : ListMap { List[] map; public IntListMap(int count) { map = Array.ConvertAll(new bool[count], _ => new List()); } public override TValue[] this[int key] => map[key].ToArray(); public override void Add(int key, TValue value) => map[key].Add(value); } public class GridListMap : ListMap { List[][] map; public GridListMap(int height, int width) { map = Array.ConvertAll(new bool[height], _ => Array.ConvertAll(new bool[width], __ => new List())); } public override TValue[] this[Point key] => map[key.i][key.j].ToArray(); public override void Add(Point key, TValue value) => map[key.i][key.j].Add(value); } public class HashListMap : ListMap { List[] map; Func ToHash; public HashListMap(int count, Func toHash) { map = Array.ConvertAll(new bool[count], _ => new List()); ToHash = toHash; } public override TValue[] this[TKey key] => map[ToHash(key)].ToArray(); public override void Add(TKey key, TValue value) => map[ToHash(key)].Add(value); } public static class GraphConsole { const char Road = '.'; const char Wall = '#'; static int[] Read() => Array.ConvertAll(Console.ReadLine().Split(), int.Parse); public static Point ReadPoint() { return Point.Parse(Console.ReadLine()); } public static Edge[] ReadEdges(int count) { return Array.ConvertAll(new bool[count], _ => EdgeHelper.ToEdge(Read())); } public static string[] ReadGrid(int height) { return Array.ConvertAll(new bool[height], _ => Console.ReadLine()); } public static char[][] ReadGridAsChar(int height) { return Array.ConvertAll(new bool[height], _ => Console.ReadLine().ToCharArray()); } public static int[][] ReadGridAsInt(int height) { return Array.ConvertAll(new bool[height], _ => Read()); } public static string[] ReadEnclosedGrid(ref int height, ref int width, char c = '#', int delta = 1) { var h = height + 2 * delta; var w = width + 2 * delta; var cw = new string(c, w); var cd = new string(c, delta); var s = new string[h]; for (int i = 0; i < delta; ++i) s[delta + height + i] = s[i] = cw; for (int i = 0; i < height; ++i) s[delta + i] = cd + Console.ReadLine() + cd; (height, width) = (h, w); return s; } } public static class GraphConvert { public static void UnweightedEdgesToMap(ListMap map, Edge[] edges, bool directed) { foreach (var e in edges) { map.Add(e.From, e.To); if (!directed) map.Add(e.To, e.From); } } public static void WeightedEdgesToMap(ListMap> map, Edge[] edges, bool directed) { foreach (var e in edges) { map.Add(e.From, e); if (!directed) map.Add(e.To, e.Reverse()); } } } public static class GridHelper { public static T GetValue(this T[,] a, Point p) => a[p.i, p.j]; public static void SetValue(this T[,] a, Point p, T value) => a[p.i, p.j] = value; public static T GetValue(this T[][] a, Point p) => a[p.i][p.j]; public static void SetValue(this T[][] a, Point p, T value) => a[p.i][p.j] = value; public static char GetValue(this string[] s, Point p) => s[p.i][p.j]; public static Point FindValue(T[][] a, T value) { var ec = EqualityComparer.Default; var (h, w) = (a.Length, a[0].Length); for (int i = 0; i < h; ++i) for (int j = 0; j < w; ++j) if (ec.Equals(a[i][j], value)) return new Point(i, j); return new Point(-1, -1); } public static Point FindValue(string[] s, char c) { var (h, w) = (s.Length, s[0].Length); for (int i = 0; i < h; ++i) for (int j = 0; j < w; ++j) if (s[i][j] == c) return new Point(i, j); return new Point(-1, -1); } public static int ToHash(Point p, int width) => p.i * width + p.j; public static Point FromHash(int hash, int width) => new Point(hash / width, hash % width); public static Func CreateToHash(int width) => p => p.i * width + p.j; public static Func CreateFromHash(int width) => hash => new Point(hash / width, hash % width); public static void EncloseGrid(ref int height, ref int width, ref T[][] a, T value, int delta = 1) { var h = height + 2 * delta; var w = width + 2 * delta; var t = Array.ConvertAll(new bool[h], _ => Array.ConvertAll(new bool[w], __ => value)); for (int i = 0; i < height; ++i) for (int j = 0; j < width; ++j) t[delta + i][delta + j] = a[i][j]; (height, width, a) = (h, w, t); } public static void EncloseGrid(ref int height, ref int width, ref string[] s, char c = '#', int delta = 1) { var h = height + 2 * delta; var w = width + 2 * delta; var cw = new string(c, w); var cd = new string(c, delta); var t = new string[h]; for (int i = 0; i < delta; ++i) t[delta + height + i] = t[i] = cw; for (int i = 0; i < height; ++i) t[delta + i] = cd + s[i] + cd; (height, width, s) = (h, w, t); } public static T[][] Rotate180(T[][] a) { var (h, w) = (a.Length, a[0].Length); var r = Array.ConvertAll(new bool[h], _ => new T[w]); for (int i = 0; i < h; ++i) for (int j = 0; j < w; ++j) r[i][j] = a[h - 1 - i][w - 1 - j]; return r; } public static T[][] RotateLeft(T[][] a) { var (h, w) = (a.Length, a[0].Length); var r = Array.ConvertAll(new bool[w], _ => new T[h]); for (int i = 0; i < w; ++i) for (int j = 0; j < h; ++j) r[i][j] = a[j][w - 1 - i]; return r; } public static T[][] RotateRight(T[][] a) { var (h, w) = (a.Length, a[0].Length); var r = Array.ConvertAll(new bool[w], _ => new T[h]); for (int i = 0; i < w; ++i) for (int j = 0; j < h; ++j) r[i][j] = a[h - 1 - j][i]; return r; } public static string[] Rotate180(string[] s) { var h = s.Length; var r = new string[h]; for (int i = 0; i < h; ++i) { var cs = s[h - 1 - i].ToCharArray(); Array.Reverse(cs); r[i] = new string(cs); } return r; } public static string[] RotateLeft(string[] s) { var (h, w) = (s.Length, s[0].Length); var r = new string[w]; for (int i = 0; i < w; ++i) { var cs = new char[h]; for (int j = 0; j < h; ++j) cs[j] = s[j][w - 1 - i]; r[i] = new string(cs); } return r; } public static string[] RotateRight(string[] s) { var (h, w) = (s.Length, s[0].Length); var r = new string[w]; for (int i = 0; i < w; ++i) { var cs = new char[h]; for (int j = 0; j < h; ++j) cs[j] = s[h - 1 - j][i]; r[i] = new string(cs); } return r; } } /// /// 優先度付きキューを表します。 /// /// オブジェクトの型。 /// /// 二分ヒープによる実装です。
/// 内部では 1-indexed のため、raw array を直接ソートする用途では使われません。 ///
public class PriorityQueue { public static PriorityQueue Create(bool descending = false) { var c = Comparer.Default; return descending ? new PriorityQueue((x, y) => c.Compare(y, x)) : new PriorityQueue(c.Compare); } public static PriorityQueue Create(Func keySelector, bool descending = false) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); var c = Comparer.Default; return descending ? new PriorityQueue((x, y) => c.Compare(keySelector(y), keySelector(x))) : new PriorityQueue((x, y) => c.Compare(keySelector(x), keySelector(y))); } public static PriorityQueue CreateWithKey(Func keySelector, bool descending = false) { var c = Comparer.Default; return descending ? new PriorityQueue(keySelector, (x, y) => c.Compare(y.key, x.key)) : new PriorityQueue(keySelector, (x, y) => c.Compare(x.key, y.key)); } List l = new List { default }; Comparison c; public T First { get { if (l.Count <= 1) throw new InvalidOperationException("The heap is empty."); return l[1]; } } public int Count => l.Count - 1; public bool Any => l.Count > 1; internal PriorityQueue(Comparison comparison) { c = comparison ?? throw new ArgumentNullException(nameof(comparison)); } // x の親: x/2 // x の子: 2x, 2x+1 void UpHeap(int i) { for (int j; (j = i >> 1) > 0 && c(l[j], l[i]) > 0; i = j) (l[i], l[j]) = (l[j], l[i]); } void DownHeap(int i) { for (int j; (j = i << 1) < l.Count; i = j) { if (j + 1 < l.Count && c(l[j], l[j + 1]) > 0) j++; if (c(l[i], l[j]) > 0) (l[i], l[j]) = (l[j], l[i]); else break; } } public void Push(T value) { l.Add(value); UpHeap(l.Count - 1); } public void PushRange(IEnumerable values) { if (values != null) foreach (var v in values) Push(v); } public T Pop() { if (l.Count <= 1) throw new InvalidOperationException("The heap is empty."); var r = l[1]; l[1] = l[l.Count - 1]; l.RemoveAt(l.Count - 1); DownHeap(1); return r; } } // キーをキャッシュすることにより、キーが不変であることを保証します。 public class PriorityQueue : PriorityQueue<(T value, TKey key)> { Func KeySelector; internal PriorityQueue(Func keySelector, Comparison<(T value, TKey key)> comparison) : base(comparison) { KeySelector = keySelector ?? throw new ArgumentNullException(nameof(keySelector)); } public void Push(T value) { Push((value, KeySelector(value))); } public void PushRange(IEnumerable values) { if (values != null) foreach (var v in values) Push(v); } } }