using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BlackHoleDetection
{
    class Program
    {
        static void Main(string[] args)
        {
            // Определение размера графа (N = 10)
            const int N = 10;

            // Матрица смежности графа (10x10)
            int[,] graphInitial = new int[N, N]
            {
                {0, 1, 1, 0, 0, 0, 0, 0, 1, 0}, // Вершина 0
                {0, 0, 0, 0, 0, 1, 1, 0, 1, 0}, // Вершина 1
                {0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, // Вершина 2
                {0, 0, 1, 0, 0, 0, 0, 0, 1, 0}, // Вершина 3
                {0, 0, 0, 1, 0, 1, 0, 0, 1, 0}, // Вершина 4
                {0, 0, 0, 0, 0, 0, 1, 0, 0, 1}, // Вершина 5
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Вершина 6
                {0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, // Вершина 7
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Вершина 8
                {0, 0, 0, 0, 0, 0, 0, 0, 1, 0}  // Вершина 9
            };

            // Последовательный поиск черных дыр
            Console.WriteLine("Последовательный поиск черных дыр:");
            var sequentialBlackHoles = FindBlackHoles(graphInitial, N, useParallel: false);
            PrintBlackHoles(sequentialBlackHoles);

            // Параллельный поиск черных дыр
            Console.WriteLine("\nПараллельный поиск черных дыр:");
            var parallelBlackHoles = FindBlackHoles(graphInitial, N, useParallel: true);
            PrintBlackHoles(parallelBlackHoles);

            // Ожидание завершения программы
            Console.WriteLine("\nНажмите любую клавишу для выхода...");
            Console.ReadKey();
        }

        /// <summary>
        /// Находит все чёрные дыры в графе.
        /// </summary>
        /// <param name="graph">Матрица смежности графа.</param>
        /// <param name="size">Количество вершин в графе.</param>
        /// <param name="useParallel">Флаг использования параллельной обработки.</param>
        /// <returns>Список найденных чёрных дыр.</returns>
        static List<List<int>> FindBlackHoles(int[,] graph, int size, bool useParallel)
        {
            List<List<int>> blackHoles = new List<List<int>>();
            object lockObj = new object();

            // Генерация всех возможных подмножеств вершин размером >= 2
            List<List<int>> allSubsets = new List<List<int>>();
            for (int subsetSize = 2; subsetSize <= size; subsetSize++)
            {
                allSubsets.AddRange(GetCombinations(Enumerable.Range(0, size).ToList(), subsetSize));
            }

            if (useParallel)
            {
                // Параллельная обработка
                Parallel.ForEach(allSubsets, subset =>
                {
                    if (IsBlackHole(subset, graph))
                    {
                        lock (lockObj)
                        {
                            blackHoles.Add(new List<int>(subset));
                        }
                    }
                });
            }
            else
            {
                // Последовательная обработка
                foreach (var subset in allSubsets)
                {
                    if (IsBlackHole(subset, graph))
                    {
                        blackHoles.Add(subset);
                    }
                }
            }

            return blackHoles;
        }

        /// <summary>
        /// Проверяет, является ли подмножество вершин чёрной дырой.
        /// </summary>
        static bool IsBlackHole(List<int> subset, int[,] graph)
        {
            // Проверка замкнутости: все исходящие рёбра из подмножества ведут только внутри подмножества
            foreach (var vertex in subset)
            {
                for (int j = 0; j < graph.GetLength(1); j++)
                {
                    if (graph[vertex, j] == 1 && !subset.Contains(j))
                        return false;
                }
            }

            // Проверка слаботочной связности
            return IsWeaklyConnected(subset, graph);
        }

        /// <summary>
        /// Проверяет, является ли подграф с заданным подмножеством вершин слаботочно связным.
        /// </summary>
        static bool IsWeaklyConnected(List<int> subset, int[,] graph)
        {
            if (subset.Count == 0)
                return false;

            // Построение неориентированного списка смежности для подграфа
            Dictionary<int, List<int>> adjacency = new Dictionary<int, List<int>>();
            foreach (var v in subset)
            {
                adjacency[v] = new List<int>();
                for (int j = 0; j < graph.GetLength(1); j++)
                {
                    if (graph[v, j] == 1 && subset.Contains(j))
                    {
                        adjacency[v].Add(j);
                    }
                    if (graph[j, v] == 1 && subset.Contains(j))
                    {
                        adjacency[v].Add(j);
                    }
                }
            }

            // BFS для проверки связности
            HashSet<int> visited = new HashSet<int>();
            Queue<int> queue = new Queue<int>();
            queue.Enqueue(subset[0]);
            visited.Add(subset[0]);

            while (queue.Count > 0)
            {
                int current = queue.Dequeue();
                foreach (var neighbor in adjacency[current])
                {
                    if (!visited.Contains(neighbor))
                    {
                        visited.Add(neighbor);
                        queue.Enqueue(neighbor);
                    }
                }
            }

            return visited.Count == subset.Count;
        }

        /// <summary>
        /// Получает все комбинации из list размером size.
        /// </summary>
        static IEnumerable<List<int>> GetCombinations(List<int> list, int size)
        {
            if (size == 0)
                yield return new List<int>();
            else
            {
                for (int i = 0; i <= list.Count - size; i++)
                {
                    int current = list[i];
                    foreach (var tail in GetCombinations(list.Skip(i + 1).ToList(), size - 1))
                    {
                        var combination = new List<int> { current };
                        combination.AddRange(tail);
                        yield return combination;
                    }
                }
            }
        }

        /// <summary>
        /// Выводит список чёрных дыр.
        /// </summary>
        static void PrintBlackHoles(List<List<int>> blackHoles)
        {
            if (blackHoles.Count == 0)
            {
                Console.WriteLine("Чёрные дыры не найдены.");
                return;
            }

            Console.WriteLine($"Найдено чёрных дыр: {blackHoles.Count}");
            for (int i = 0; i < blackHoles.Count; i++)
            {
                var bh = blackHoles[i];
                bh.Sort();
                Console.WriteLine($"Чёрная дыра {i + 1}: {{{string.Join(", ", bh)}}}");
            }
        }
    }
}
Посмотри этот код, объясни его подробно, правильно ли он выполняет задачу? выведи еще на экран время выполнения алгоритма последовательно и параллельно