几个月前,我参加了CodeForces的竞赛,自那以后我一直在尝试不同的解决方案。
问题基本上由包含有向图的节点和边的输入组成。基本上,如果:A指向B,B指向C,C指向D,那么我们可以说A也指向C,而D则指向B,指向D.这个想法。
我现在的问题是,我尝试了一些不同的解决方案,其中一些解决方案但仍遇到时间限制。另一个是我最好的尝试,使其有效,但它在一些测试失败。不幸的是,我不允许看一下测试。
这是我的代码的骨架,如果你不想在这里查看整个代码,下面会提到TransitiveClosure()函数:
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;
}
}
}
这是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);
}
}
}
我也尝试以递归方式进行,但这恰好是错误的,我不确定它背后的逻辑部分是什么错误:
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;
}
我的代码使用邻接矩阵的其他尝试,以及其他可以在此处找到,如果相关:http://pastebin.com/A0a186V5。
我很感激您对此的意见,我可以采取哪些措施加快速度。此外,如果你能告诉我我能做些什么来让我的第二次递归尝试工作。请原谅我在代码中缺少评论。
我不确定这可以实现的最佳复杂性,所以请引导我走上正轨。也许还有一些我不知道的其他算法,我可以看一下。
谢谢你,对不起这篇长篇文章感到抱歉!