1

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!

Guy Coder
  • 24,501
  • 8
  • 71
  • 136

0 Answers0