A few months ago, I had taken part in a contest on CodeForces, and I've been trying different solutions to solve it since.
The problem basically consists of inputs containing nodes and edges of a directed graph. Basically, if: A is pointing to B, and B is pointing to C, and C is pointing to D, then we can say that A is also pointing to C, and D. So goes for B, pointing to D. You get the idea.
My problem right now is, I have attempted a few different solutions, a few of them worked but got stuck with a Time Limit. The other one is my best attempt to make it efficient, but it fails on some tests. Unfortunately, I am not allowed to take a look at the test.
This is the skeleton for my code, the TransitiveClosure() function is mentioned below if you do not want to look at the whole code here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace D.Find_Path3
{
class Program
{
public class ReachabilityGraph
{
// placeholder
public void TransitiveClosure() { }
private Node[] _nodesArr;
private HashSet<Node> _validNodes;
private bool[,] _transitiveClosure;
private int _nodes;
private int _edges;
public int Nodes { get { return _nodes; } }
public int Edges { get { return _edges; } }
public ReachabilityGraph(int nodes)
{
_nodes = nodes;
_nodesArr = new Node[nodes];
_validNodes = new HashSet<Node>();
_transitiveClosure = new bool[nodes, nodes];
}
public void AddEdge(int u, int v)
{
if (_nodesArr[u] == null)
_nodesArr[u] = new Node(u);
_validNodes.Add(_nodesArr[u]);
if (_nodesArr[v] == null)
_nodesArr[v] = new Node(v);
if (_nodesArr[u].AddChild(_nodesArr[v]))
++_edges;
}
public bool IsConnected(int u, int v)
{
return _transitiveClosure[u, v];
}
class Node
{
private Lazy<HashSet<Node>> children = new Lazy<HashSet<Node>>(() => new HashSet<Node>());
public int Value { get; private set; }
public IEnumerable<Node> Children { get { return children.IsValueCreated ? children.Value : Enumerable.Empty<Node>(); } }
public bool HasChildren { get { return children.IsValueCreated && children.Value.Count != 0; } }
public Node(int value)
{
Value = value;
}
public bool AddChild(Node u)
{
return children.Value.Add(u);
}
public override bool Equals(object obj)
{
var other = obj as Node;
return other != null && other.Value == Value;
}
public override int GetHashCode()
{
return Value;
}
public override string ToString()
{
return Value.ToString();
}
public static implicit operator int(Node n)
{
return n.Value;
}
}
}
static void Main(string[] args)
{
// N: number of nodes
// M: number of edges
var NM = new int[2];
GetIntsFromConsole(NM);
var graph = new ReachabilityGraph(NM[0]);
// add edges to graph
var uv = new int[2];
for (var i = 0; i < NM[1]; ++i)
{
GetIntsFromConsole(uv);
graph.AddEdge(uv[0] - 1, uv[1] - 1);
}
graph.TransitiveClosure();
// how many queries
var Q = int.Parse(Console.ReadLine());
for (var i = 0; i < Q; ++i)
{
GetIntsFromConsole(uv);
Console.WriteLine(graph.IsConnected(uv[0] - 1, uv[1] - 1) ? "YES" : "NO");
}
}
static void GetIntsFromConsole(int[] array)
{
var query = Console
.ReadLine()
.Split(' ')
.Select(a => int.Parse(a));
int i = 0;
foreach (var item in query)
array[i++] = item;
}
}
}
This is the working, but slow attempt of TransitiveClosure:
public void TransitiveClosure()
{
foreach (var u in _validNodes)
{
var stack = new Stack<Node>();
stack.Push(u);
while (stack.Any())
{
var v = stack.Pop();
_transitiveClosure[u, v] = true;
foreach (var temp in v.Children)
if (!_transitiveClosure[u, temp])
stack.Push(temp);
}
}
}
I also tried to do it recursively, but this happens to be wrong, I am not sure what part of the logic is wrong behind it:
public void TransitiveClosure()
{
var stack = new Stack<Node>();
foreach (var u in _validNodes)
{
if (!_transitiveClosure[u, u])
{
TransitiveClosure(u, stack);
stack.Clear();
}
}
}
private Stack<Node> TransitiveClosure(Node u, Stack<Node> subStack)
{
if (u.HasChildren && !_transitiveClosure[u, u])
{
_transitiveClosure[u, u] = true;
var superStack = new Stack<Node>();
foreach (var child in u.Children)
{
var result = TransitiveClosure(child, subStack);
while (result.Any())
{
var v = result.Pop();
superStack.Push(v);
_transitiveClosure[u, v] = true;
}
}
subStack = superStack;
}
subStack.Push(u);
return subStack;
}
Other attempts of my codes using Adjacency Matrices, and others can be found here, if relevant: http://pastebin.com/A0a186V5.
I would appreciate your input on this, on what I can do to make it faster. Also, if you can tell me what I can do to make my second recursive attempt to work. Please pardon my lack of comments in the code.
I am not sure about what is the best complexity that can be achieved with this, so please guide me on the right track. Maybe there is some other algorithm that I am not aware of that I can look at.
Thank you, and sorry for the long post!