Объединить меньшие прямоугольники в большие

у меня проблема, когда мне нужно объединить маленькие квадраты в большие прямоугольники. Скажем, у меня есть 2D-сетка, заполненная случайными 1 и 0:

1 0 1 1 0
1 0 1 1 1
0 1 0 1 1
0 1 0 1 1
0 0 1 0 0

1 представляют области, которые заполнены, и я рисую их на экране вниз по линии в виде квадратов. Однако для этой проблемы мне нужно сначала сопоставить их в прямоугольники. В примере показано, что 1 в верхнем левом углу ->

1
1

можно соединить в прямоугольник.

Я думаю, что должно быть достаточно объяснить, что мне нужно. Однако предпочтительно, чтобы конкретный квадрат не использовался более чем в одном прямоугольнике. Кроме того, это не должен быть лучший случай с наименьшим количеством прямоугольников, просто лучший случай с меньшим количеством прямоугольников. Прямоугольники 1x1 также разрешены, если бы это упростило дело.

любое понимание того, с чего я мог бы начать, или даже решение будет оценено.

Если вы хотите знать причину этой проблемы, я работаю на level builder для игры, над которой я работаю, и я хочу уменьшить количество вершин. Я думал, что начну с квадратов, потому что они будут простыми, но даже это ошеломляет меня.

Спасибо, что нашли время для чтения!

2 ответов


простым подходом было бы искать соседние квадраты и превращать их в прямоугольники. Для этого сначала пройдите горизонтально через сетку и соедините горизонтально соседние квадраты, затем пройдите через сетку вертикально и соедините вертикально соседние квадраты.

считаем:

H = кусок горизонтального прямоугольника

V = кусок вертикального прямоугольника

ваш исходный пример:

 1 0 1 1 0
 1 0 1 1 1
 0 1 0 1 1
 0 1 0 1 1
 0 0 1 0 0

получилось бы в:

V 0 H H 0
V 0 H H H
0 V 0 H H
0 V 0 H H
0 0 1 0 0

этот подход не является оптимальным, но он превратит квадраты в прямоугольники, если это возможно сделать с учетом 2D-сетки.


Я использую 2 шага, чтобы уменьшить количество коллайдеров в моей игре. Объединение горизонтально последовательных типов, затем объединение вертикально типов, имеющих одинаковую ширину.

steps

код находится в процессе работы, но, похоже, работает~

public class Tiles
{
    public char Type { get; set; }
    public int X { get; set; }
    public int Y { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }

    public override string ToString()
    {
        return $@"({X}, {Y}, {Width}, {Height}) '{Type}'";
    }
}

public class TilesFromStrings
{
    private List<Tiles> Result = new List<Tiles>();

    public IEnumerable<Tiles> Create(params string[] lines)
    {
        Result.Clear();

        CreateMergedHorizontalTiles(lines);
        MergeVerticallyTilesWithSameWidth();

        return Result.Where(f => f.Height > 0);
    }

    private void MergeVerticallyTilesWithSameWidth()
    {
        foreach (var current in Result)
        {
            foreach (var other in Result)
            {
                if (other.Y + other.Height == current.Y
                        && other.X == current.X
                        && other.Height > 0
                        && current.Height > 0)
                {
                    if (other.Type == current.Type)
                    {
                        if (other.Width == current.Width)
                        {
                            current.Height--;
                            current.Y++;
                            other.Height++;
                            break;
                        }
                    }
                }
            }
        }
    }

    private void CreateMergedHorizontalTiles(string[] tiles)
    {
        Tiles currentRect = null;
        var lastColumnIndex = tiles[0].Length - 1;
        for (int rowIndex = 0; rowIndex < tiles.Length; rowIndex++)
        {
            for (int columnIndex = 0; columnIndex < tiles[rowIndex].Length; columnIndex++)
            {
                var currentType = tiles[rowIndex][columnIndex];

                if (columnIndex == 0)
                {
                    currentRect = new Tiles
                    {
                        X = columnIndex + 1,
                        Y = rowIndex + 1,
                        Width = 1,
                        Height = 1,
                        Type = currentType
                    };

                    continue;
                }

                if (columnIndex == lastColumnIndex)
                {
                    if (currentRect.Type == currentType)
                    {
                        Result.Add(currentRect);
                        currentRect.Width++;
                    }
                    else
                    {
                        Result.Add(currentRect);
                        currentRect = new Tiles
                        {
                            X = columnIndex + 1,
                            Y = rowIndex + 1,
                            Width = 1,
                            Height = 1,
                            Type = currentType
                        };
                        Result.Add(currentRect);
                    }

                    continue;
                }

                if (currentRect.Type == currentType)
                {
                    currentRect.Width++;
                }
                else
                {
                    Result.Add(currentRect);
                    currentRect = new Tiles
                    {
                        X = columnIndex + 1,
                        Y = rowIndex + 1,
                        Width = 1,
                        Height = 1,
                        Type = currentType
                    };
                }
            }
        }
    }
}