2

Задача пробного раунда Яндекс Контеста https://contest.yandex.ru/yacup/contest/19036/problems/E/

Сеть компании состоит из серверов, каждый из которых имеет уникальный целочисленный идентификатор. N пар серверов соединены друг с другом. Соединённые серверы образуют кластеры: два сервера относятся к одному и тому же кластеру, если от одного из них можно добраться до другого, перемещаясь по связям. Периодически возникает необходимость скачать определённый файл на некоторый сервер. В сети работает сервис, аналогичный торрент-трекеру, который может сообщить, на каких серверах уже имеется необходимый файл. Проблема, однако, заключается в том, что любой сервер может скачивать файлы только с серверов в его кластере. Составьте программу, которая будет анализировать конфигурацию сети и сообщать, из каких источников определённый сервер может скачать необходимый файл.

Дальше приведу скриншот, т.к. сюда некорректно вставляется

Задача

Вот мой код, постарался понятнее его переписать Логика программы такая: Считываю связи между серверами, с помощью поиска в глубину ищу среди них узлы, после считываю с консоли запросы и сразу же смотрю, есть ли они в том же кластере, что и Xi. Есть 3 идеи на оптимизацию:

  1. При вводе связей сразу создавать подкластеры(A1,B1),(A2,B2)..., если A2 или B2 содержится в первом кластере то добавить B2 или A2 соответсвенно в кластер, потом ещё раз пройтись и объединить уже подкластеры, но не очень понятно как это сделать правильно, пока ещё размышляю.
  2. При считывании запросов можно сначала всё считать, потом объединить одинаковые Xi, ведь в условии не сказано что они не повторяются, но тогда надо как-то будет следить за порядком вывода, что в конечном итоге мне кажется не даст существенного ускорения
  3. Распараллелить программу? Кто-то в курсе, дают проверяющие машины несколько процессоров или нет?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace Yandex2020_1 { class Program { static void Main(string[] args) { int n = Convert.ToInt32(Console.ReadLine()); List< Tuple<int, int>> linkList = new List<Tuple<int, int>>(); HashSet<int> setNodes = new HashSet<int>(); HashSet<Node> nodes = new HashSet<Node>(); for (int i = 0; i < n; i++) { string[] nums_strings = Console.ReadLine().Split(); int a = Convert.ToInt32(nums_strings[0]); int b = Convert.ToInt32(nums_strings[1]); linkList.Add(new Tuple<int, int>(a, b)); setNodes.Add(a);setNodes.Add(b); } MyStopwatch stopWatch = new MyStopwatch();

        foreach (int i in setNodes) 
            nodes.Add(new Node(i));
        foreach(Tuple&lt;int, int&gt; tuple in linkList)
        {
            Node node = nodes.Where(p=&gt;p.Name==tuple.Item1).ToList()[0];
            node.AddChildren(nodes.Where(p =&gt; p.Name == tuple.Item2).ToList()[0]);
        }
        DepthFirstSearch dFS = new DepthFirstSearch();
        HashSet&lt;HashSet&lt;int&gt;&gt; clusters=new HashSet&lt;HashSet&lt;int&gt;&gt;(); //Сет кластеров
        Node nodeSearch= nodes.Where(x =&gt; !dFS.visited.Contains(x)).ToList().FirstOrDefault(); 
        while(nodeSearch!=null)
        {
            clusters.Add(dFS.DFS(nodeSearch));
            nodeSearch = nodes.Where(x =&gt; !dFS.visited.Contains(x)).ToList().FirstOrDefault();//берём первый непосещённый узел
        }
        int q = Convert.ToInt32(Console.ReadLine());
        List&lt;string&gt; outS= new List&lt;string&gt;();


         //поиск принадлежности к кластерам
        for (int i = 0; i &lt; q; i++)
        {
            string[] nums_strings = Console.ReadLine().Split();
            int a = Convert.ToInt32(nums_strings[0]);

            //ищем кластер в котором сервер, на который нужно скачать
            HashSet&lt;int&gt; cluster = clusters.Where(x =&gt; x.Contains(a)).ToList()[0];
            nums_strings = Console.ReadLine().Split();
            int k = 0;
            string temp = &quot;&quot;;

            //записываем все сервера, к которым есть доступ сразу в строку для вывода
            foreach (string s in nums_strings)
            {
                if (cluster.Contains(Convert.ToInt32(s)))
                {
                    k++;
                    temp += s+&quot; &quot;;
                }
            }
            outS.Add(k.ToString()+&quot; &quot; + temp.Trim());
        }


        foreach (string s in outS)
            Console.WriteLine(s);


    }




}


public class MyStopwatch:Stopwatch
{
    List&lt;string&gt; line =new List&lt;string&gt;();
    public void Rec(string name)
    {
        TimeSpan ts = Elapsed;
        string elapsedTime = String.Format(&quot;{0:00}:{1:00}:{2:00}.{3:00}&quot;,
        ts.Hours, ts.Minutes, ts.Seconds,
        ts.Milliseconds / 10);
        line.Add(name + &quot;: RunTime &quot; + elapsedTime);
    }
    public void Write()
    {
        foreach(string s in line)
            Console.WriteLine(s);  
    }

}


class Node :  IComparable&lt;Node&gt;
{
    /// Имя вершины.
    public int Name { get; }
    /// Список соседних вершин.
    public List&lt;Node&gt; Children { get; }

    public Node(int name)
    {
        Name = name;
        Children = new List&lt;Node&gt;();
    }

    /// Добавляет новую соседнюю вершину.
    /// bidirect-&gt;Неориентированое ребро
    public Node AddChildren(Node node, bool bidirect = true)
    {
        Children.Add(node);
        if (bidirect)
        {
            node.Children.Add(this);
        }
        return this;
    }
    public int CompareTo(Node comparePart)
    {
        // A null value means that this object is greater.
        if (comparePart == null)
            return 1;

        else
            return this.Name.CompareTo(comparePart.Name);
    }
}

//обход 
class DepthFirstSearch
{
    // Список посещенных вершин
    public HashSet&lt;Node&gt; visited= new HashSet&lt;Node&gt;();
    // Путь из начальной вершины в целевую.
    private HashSet&lt;int&gt; path;

    public HashSet&lt;int&gt; DFS(Node start)
    {
        path = new HashSet&lt;int&gt;();
        Search(start);
        path.Add(start.Name);
        return path;
    }

    private bool Search(Node node)
    {

        visited.Add(node);
        foreach (var child in node.Children.Where(x =&gt; !visited.Contains(x)))
        {
            Search(child);
            path.Add(child.Name);
        }
        return false;
    }
}

}

Новый вариант

using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace Yandex2020_1 { class Program { static void Main(string[] args) { int n = int.Parse(Console.ReadLine()); List< Tuple<int, int>> linkList = new List<Tuple<int, int>>(); HashSet<int> setNodes = new HashSet<int>(); HashSet<Node> nodes = new HashSet<Node>(); for (int i = 0; i < n; i++) { string[] nums_strings = Console.ReadLine().Split(); int a = int.Parse(nums_strings[0]); int b = int.Parse(nums_strings[1]); linkList.Add(new Tuple<int, int>(a, b)); setNodes.Add(a);setNodes.Add(b); }

        foreach (int i in setNodes) 
            nodes.Add(new Node(i));
        foreach(Tuple&lt;int, int&gt; tuple in linkList)
        {
            Node node = nodes.Where(p=&gt;p.Name==tuple.Item1).First();
            node.AddChildren(nodes.Where(p =&gt; p.Name == tuple.Item2).First());
        }
        DepthFirstSearch dFS = new DepthFirstSearch();
        HashSet&lt;HashSet&lt;int&gt;&gt; clusters=new HashSet&lt;HashSet&lt;int&gt;&gt;(); //Сет кластеров
        Node nodeSearch= nodes.Where(x =&gt; !dFS.visited.Contains(x)).FirstOrDefault(); 
        while(nodeSearch!=null)
        {
            clusters.Add(dFS.DFS(nodeSearch));
            nodeSearch = nodes.Where(x =&gt; !dFS.visited.Contains(x)).FirstOrDefault();//берём первый непосещённый узел
        }
        int q = int.Parse(Console.ReadLine());
        List&lt;string&gt; outS= new List&lt;string&gt;();                                 


         //поиск принадлежности к кластерам
        for (int i = 0; i &lt; q; i++)
        {
            int a = int.Parse(Console.ReadLine().Split()[0]);

            //ищем кластер в котором сервер, на который нужно скачать
            HashSet&lt;int&gt; cluster = clusters.Where(x =&gt; x.Contains(a)).ToList().First();
            string[] nums_strings = Console.ReadLine().Split();
            int k = 0;
            string temp = &quot;&quot;;

            //записываем все сервера, к которым есть доступ сразу в строку для вывода
            foreach (string s in nums_strings)
            {
                if (cluster.Contains(int.Parse(s)))
                {
                    k++;
                    temp += s+&quot; &quot;;
                }
            }
            outS.Add(k+&quot; &quot; + temp.Trim());
        }


        foreach (string s in outS)
            Console.WriteLine(string.Join(Environment.NewLine, s));


    }




}



class Node :  IComparable&lt;Node&gt;
{
    /// Имя вершины.
    public int Name { get; }
    /// Список соседних вершин.
    public List&lt;Node&gt; Children { get; }

    public Node(int name)
    {
        Name = name;
        Children = new List&lt;Node&gt;();
    }

    /// Добавляет новую соседнюю вершину.
    /// bidirect-&gt;Неориентированое ребро
    public Node AddChildren(Node node, bool bidirect = true)
    {
        Children.Add(node);
        if (bidirect)
        {
            node.Children.Add(this);
        }
        return this;
    }
    public int CompareTo(Node comparePart)
    {
        // A null value means that this object is greater.
        if (comparePart == null)
            return 1;

        else
            return this.Name.CompareTo(comparePart.Name);
    }
}

//обход 
class DepthFirstSearch
{
    // Список посещенных вершин
    public HashSet&lt;Node&gt; visited= new HashSet&lt;Node&gt;();
    // Путь из начальной вершины в целевую.
    private HashSet&lt;int&gt; path;

    public HashSet&lt;int&gt; DFS(Node start)
    {
        path = new HashSet&lt;int&gt;();
        Search(start);
        path.Add(start.Name);
        return path;
    }

    private bool Search(Node node)
    {

        visited.Add(node);
        foreach (var child in node.Children.Where(x =&gt; !visited.Contains(x)))
        {
            Search(child);
            path.Add(child.Name);
        }
        return false;
    }
}

}

  • Вот такой паттерн foreach (string s in outS) Console.WriteLine(s); можно преобразовать в вот такой Console.WriteLine(string.Join(Environment.NewLine, outS). Но это на ваш вкус, просто делюсь. Плюс преобразования в том, что вызов операции вывода в консоль будет один, а не много. Минус в том, что в памяти появится большая строка, содержащая все строки из массива. 2) Convert.ToInt32 можно заменить на int.Parse в вашем случае, потому что исходные данные - string.
  • – aepot Sep 23 '20 at 11:50
  • nodes.Where(..).ToList()[0] можно заменить на nodes.Where(...).First() 4) int q = Convert.ToInt32(Console.ReadLine()); - посмотрите безотказный ввод числа, можно сделать красиво. 5) outS.Add(k.ToString()+" " + temp.Trim()); - в операциях конкатенации вызывать явно .ToString() не нужно, он будет вызван неявно автоматически.
  • – aepot Sep 23 '20 at 12:00
  • .ToList().FirstOrDefault() тоже можно заменить на .FirstOrDefault(). Работать должно быстрее.
  • – aepot Sep 23 '20 at 12:51