数组墙 最详细的解题报告

题目

随机给定一个整型数组,每个数组中的数字代表数组所在位置墙的高度,问这个数组所能拼凑的最大矩形墙的面积为多少。

示例

  • 输入:
  • 输出:16

提示

数组{2, 1, 6, 5, 4, 7, 2}可以描述为:

2 1 6 5 4 7 2
\(\color{#000000}{*}\)
\(\color{#000000}{*}\) \(\color{#000000}{*}\)
\(\color{#000000}{*}\) \(\color{#000000}{*}\) \(\color{#000000}{*}\)
\(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\)
\(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\)
\(\color{#000000}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#000000}{*}\)
\(\color{#000000}{*}\) \(\color{#000000}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#000000}{*}\)

其中,

  • 第一行的数字表示数组中对应的值
  • 每一列中\(\color{#000000}{*}\)的个数加上\(\color{#FF3030}{*}\)的个数之和等于第一行中数组的值
  • \(\color{#FF3030}{*}\)表示最大的矩形墙面积

解题思路

  1. 将原数组array复制一份到数组copy
  2. 新建一个数组area用来存储包含当前列的最大面积,初始值为0
  3. 将数组copy0元素切割成多个子数组用Coordinate对象来表示,其中Coordinate.startIndex表示子数组的起始下标,Coordinate.endIndex表示子数组的结束下标(但不包含),Coordinate.minValue表示子数组的最小值
  4. 将数组copy[i]中的值减去Coordinate.minValue,其中Coordinate.startIndex <= i < Coordinate.endIndex,根据数组area的值area[i] = Math.max(area[i], (array[i] - copy[i]) * (Coordinate.endIndex - Coordinate.startIndex))
  5. 重复3)4)遍历所有的Coordinate对象,直到按0元素切割不能获得有效Coordinate

具体算法(Java版)

public class FindMaxRectangle {

    public static void main(String[] args) {
        // 随机生成数组
        int[] array = generateArray(20);
        // 将数组打印出来
        System.out.println("当前数组:" + arrayToString(array));
        // 打印数组墙
        printArray(array);
        // 输出最大矩形墙的面积
        System.out.println("数组最大矩形墙的面积为:" + findMax(array));
    }

    /**
     * 查找最大矩形墙的面积
     */
    private static int findMax(int[] array) {
        int[] copy = new int[array.length]; // 记录运行时数组中的值
        int[] area = new int[array.length]; // 记录最大矩形墙的面积
        Queue<List<Coordinate>> queue = new LinkedList<>();
        for (int i = 0; i < array.length; i++) {
            copy[i] = array[i];
            area[i] = 0;
        }
        queue.offer(divideArray(copy));
        while (!queue.isEmpty()) {
            List<Coordinate> coordinates = queue.poll();
            // 没有任何有效的子数组
            if (coordinates.size() == 0)
                break;
            for (Coordinate coordinate : coordinates) {
                for (int i = coordinate.getStartIndex(); i < coordinate.getEndIndex(); i++) {
                    if (copy[i] > 0) {
                        // 减去最小值minValue
                        copy[i] -= coordinate.getMinValue();
                    }
                    // 计算子数组对应的最大矩阵墙面积
                    int value = (array[i] - copy[i]) *
                            (coordinate.getEndIndex() - coordinate.getStartIndex());
                    // 更新最大矩阵墙的面积
                    area[i] = Math.max(value, area[i]);
                }
            }
            queue.offer(divideArray(copy));
        }
        // 查找最大的矩阵墙的面积
        int maxArea = 0;
        for (int i = 0; i < area.length; i++) {
            if (maxArea < area[i]) {
                maxArea = area[i];
            }
        }
        return maxArea;
    }

    /**
     * 将数组按照0元素切分成多个子数组,子数组用Coordinate对象来记录
     */
    private static List<Coordinate> divideArray(int[] array) {
        List<Coordinate> coordinates = new ArrayList<>();
        int startIndex = -1, endIndex = -1, minValue = Integer.MAX_VALUE;
        for (int i = 0; i < array.length; i++) {
            if (array[i] != 0) {
                if (startIndex == -1) {
                    startIndex = i;
                }
                if (array[i] < minValue) {
                    minValue = array[i];
                }
            } else {
                if (startIndex != -1) {
                    endIndex = i;
                    coordinates.add(new Coordinate(startIndex, endIndex, minValue));
                    startIndex = -1;
                    minValue = Integer.MAX_VALUE;
                }
            }
        }
        if (startIndex != -1) {
            coordinates.add(new Coordinate(startIndex, array.length, minValue));
        }
        return coordinates;
    }

    /**
     * 随机生成指定长度的数组
     */
    private static int[] generateArray(int length) {
        int[] array = new int[length];
        Random random = new Random();
        for (int i = 0; i < array.length; i++) {
            int value = random.nextInt(10);
            if (value < 0) {
                value = 0;
            }
            array[i] = value;
        }
        return array;
    }

    /**
     * 打印成数组墙
     */
    private static void printArray(int[] array) {
        int max = 0;
        for (int i = 0; i < array.length; i++) {
            if (max < array[i]) {
                max = array[i];
            }
        }
        for (int i = max; i > 0; i--) {
            for (int j = 0; j < array.length; j++) {
                if (array[j] >= i) {
                    System.out.print("* ");
                } else {
                    System.out.print("  ");
                }
            }
            System.out.println();
        }
    }

    /**
     * 将数组转换成字符串
     */
    private static String arrayToString(int[] array) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < array.length; i++) {
            stringBuilder.append(array[i]);
            if (i != array.length - 1) {
                stringBuilder.append(", ");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    /**
     * 用来记录子数组信息
     */
    static class Coordinate {
        /**
         * @param startIndex 数组的起始下标
         * @param endIndex 数组的结束下标(不包含)
         * @param minValue 数组中的最小值
         */
        public Coordinate(int startIndex, int endIndex, int minValue) {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.minValue = minValue;
        }

        private int startIndex; // 数组的起始下标
        private int endIndex; // 数组的结束下标(不包含)
        private int minValue; // 数组中的最小值

        public int getStartIndex() {
            return startIndex;
        }

        public int getEndIndex() {
            return endIndex;
        }

        public int getMinValue() {
            return minValue;
        }
    }
}

输出结果

运行结果

如果大家有什么更好的方法或者发现代码中存在bug希望可以一起交流讨论!

posted @ 2020-08-16 17:34  PinXiong  阅读(527)  评论(0编辑  收藏  举报