using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
///
/// Пара индексов для двумерного Int32 массива.
///
public struct XY
{
public XY(int x, int y)
{
this.x = x;
this.y = y;
}
public int x, y;
public int GetValue(int[,] arr)
{
return arr[x, y];
}
public override string ToString()
{
return x.ToString() + ", " + y.ToString();
}
}
public class Program
{
public static void Main(String[] args)
{
int size1 = 4000, size2 = 4000;
int[,] arr = new int[size1,size2];
Random random = new Random();
for (int x = 0; x < size1; x++)
for (int y = 0; y < size2; y++)
arr[x, y] = random.Next();
var timer = new Stopwatch();
timer.Start();
Console.WriteLine("Длина пути: " + GetLongestPathInArray(arr).Count);
timer.Stop();
Console.WriteLine("Память: " + (Process.GetCurrentProcess().WorkingSet64 / (1024 * 1024)).ToString() + " MB");
Console.WriteLine("Временя: " + timer.ElapsedMilliseconds);
//GetLongestPathInArray(arr).ForEach(x => Console.WriteLine(x.GetValue(arr)));
}
///
/// Возвращает список элементов, обарзующих наиболее длинную цепочку возрастания в данном массиве.
///
public static List GetLongestPathInArray(int[,] arr)
{
List longestPath = new List();
List[,] foundWays = new List[arr.GetLength(0),arr.GetLength(1)]; //Инициализируем вспомогательный массив уже найденных цепочек
for (int x = 0; x < arr.GetLength(0); x++)
for (int y = 0; y < arr.GetLength(1);y++)
{
List path = GetLongestPathFrom(arr, new XY(x, y), foundWays);
if (path.Count > longestPath.Count)
longestPath = path;
}
return longestPath;
}
///
/// Возвращает цепочку возрастания наибольшей длины от указанного элемента, включая его самого.
///
private static List GetLongestPathFrom(int[,] arr, XY pos, List[,] foundWays)
{
if (foundWays[pos.x, pos.y] == null) //Если мы еще не искали цепочку для этого элемента
{
List neighbours = GetNeighbours(arr, pos).FindAll(n => n.GetValue(arr) > pos.GetValue(arr)); //Получаем возможные направления поиска
List longestPath = new List();
foreach (XY neighbour in neighbours)
{
List tmpPath = GetLongestPathFrom(arr, neighbour, foundWays); //Ищем наиболее "перспективного" соседа
if (tmpPath.Count > longestPath.Count)
longestPath = tmpPath;
}
longestPath = new List(longestPath); //Необходимо клонировать, т.к. иначе будет изменяться сохраненный путь у соседа.
longestPath.Insert(0, pos); //Добавляем себя к пути соседа и сохраняем
foundWays[pos.x, pos.y] = longestPath;
return longestPath;
}
else
return foundWays[pos.x, pos.y];
//Такое "кэширование" позволяет ускорить алгоритм в несколько раз, избегая повторных рассчетов одинаковых цепочек.
}
///
/// Возвращает всех соседей указанного элемента (от 2 до 4, в зависимости от индексов).
///
private static List GetNeighbours(int[,] arr, XY indexes)
{
List result = new List(2);
if (indexes.x > 0)
result.Add(new XY(indexes.x - 1, indexes.y));
if (indexes.x < arr.GetLength(0) - 1)
result.Add(new XY(indexes.x + 1, indexes.y));
if (indexes.y > 0)
result.Add(new XY(indexes.x, indexes.y - 1));
if (indexes.y < arr.GetLength(1) - 1)
result.Add(new XY(indexes.x, indexes.y + 1));
return result;
}
}