[贪心算法]套娃式覆盖矩阵解决数字排列打印机

这是我参与更文挑战的第18天,活动详情查看: 更文挑战java

1、题目描述

1591. 奇怪的打印机 II

给你一个奇怪的打印机,它有以下两个特殊的打印规则:算法

每一次操做时,打印机会用同一种颜色打印一个矩形的形状,每次打印会覆盖矩形对应格子里本来的颜色。 一旦矩形根据上面的规则使用了一种颜色,那么 相同的颜色不能再被使用 。 给你一个初始没有颜色的 m x n 的矩形 targetGrid ,其中 targetGrid[row][col] 是位置 (row, col) 的颜色。markdown

若是你能按照上述规则打印出矩形targetGrid,请你返回 true ,不然返回 false 。oop

image-20210610143122250

2、思路分析

打印机

image-20210610142855013

  • 本题考查的就是贪心算法!有一点须要注意的是咱们在打印颜色时须要从外至内进行打印。不然会致使内部颜色打印失效。

image-20210610144009036

  • 只有保持这个维度才能确保外部被打乱的颜色能够被打印机打印。由于打印机的两个特性:一次打印矩形+一个颜色只能用一次post

  • 换句话说不通颜色咱们能够理解成不一样卡片叠加在一块儿产生的效果。最底层的就是咱们最外层的颜色。只有这样最小的最上层才会被从新渲染。spa

image-20210610152612732

判断

  • 可是本题并非让咱们去实现打印机打印的过程。仅仅是要求咱们对m*n矩阵进行判断!既然是判断是否知足这个奇葩的打印机那就稍微简单一点。
  • 首先外层的图层咱们不须要考虑,咱们只须要优先考虑内层是否知足打印需求便可

image-20210610155418436

  • 上图中黑框圈中的就是咱们说的最外层!最内层咱们分析下打印机是没法打印的,因此上述状况就是不知足打印条件。
  • 关因而否知足打印的判断也很简单。只须要判断内层矩阵内是不是同一颜色便可。

image-20210610160522062

  • 那么这种状况根据上面我说的判断依据进行判断好像是不经过的。可是这种状况很明显是能够打印的。
  • 上面也说了须要从内置外。咱们只须要先进行判断若是最内层不知足则总体不知足。最外层不须要关心,由于对第一层打印全是红色,等到对第二层打印后就会出现上面状况。因此这里须要仔细思考下!
for (Map.Entry<Integer, Direction> entry : entries) {
    Integer key = entry.getKey();
    if (sameColorAndPrintMark(key, entry.getValue(), targetGrid)) {
        value=key;
        break;
    }
}
复制代码
  • 咱们只须要对当前颜色组进行判断就能够了!最后经过对value进行判断可以删选出内层知足条件的颜色块。将颜色块从颜色集合中剔除。而后在重复此操做就能够了。

渲染

  • 上面咱们说了判断的依据。可是在代码中出现一个sameColorAndPrintMark方法。该方法是判断区间内是否颜色相同并进行打印标记的。由于颜色取值是[1,60]。因此咱们这里使用0来标记已经被其余色块打印过 。而后在判断是不是同一色块时过滤掉已经被渲染色块在进行判断若是不经过则真的没法经过了。

image-20210610165125592

  • 上面这种状况咱们在判断浅蓝色的时候经过,当进行判断橙色色块时是不经过的。由于他的范围内包含了除了已经被渲染的蓝色之外还有未被渲染的红色。因此这种状况是不能被打印的。
private boolean sameColorAndPrintMark(Integer key, Direction direction, int[][] targetGrid) {
    for (int i = direction.top; i <= direction.bottom; i++) {
        for (int j = direction.left; j <= direction.right; j++) {
            if(targetGrid[i][j]!=0&&targetGrid[i][j]!=key)
                return false;
        }
    }
    for (int i = direction.top; i <= direction.bottom; i++) {
        for (int j = direction.left; j <= direction.right; j++) {
            targetGrid[i][j] = 0;
        }
    }
    return true;
}
复制代码
  • 最终咱们一直重复渲染的步骤,直到全部的版块都被标记打印了。固然对于自己就不知足打印的状况就一直不会出现所有被标记的状况这种状况岂不是一种重复下去。这样程序不就死循环了吗
  • 固然咱们不容许这种状况发生。咱们在渲染结束后须要进行判断若是在针对剩余的色块进行渲染时没有一块能够进行渲染,那么咱们就直接断定不知足状况
if (value == -1) {
    return false;
} else {
    //剔除
    colorMap.remove(value);
}
复制代码

3、AC代码

  • 在上面分析的过程我已经基本将AC代码暴露出来了。我是经过不一样的状况讲解出了各个代码的做用。我以为这样更加的有利于咱们理解他的做用。如今我放出完成的代码供读者们参考!!!
class Direction{
    int left = 61;
    int right = -1;
    int top = 61;
    int bottom = -1;
}
public boolean isPrintable(int[][] targetGrid) {
    int[] values=new int[61];
    int n=targetGrid.length, m=targetGrid[0].length;
    Map<Integer,Direction> colorMap = new HashMap<>();
    for(int i=0; i<n; i++){
        for(int j=0; j<m; j++){
            int val=targetGrid[i][j];
            Direction direction = null;
            if (colorMap.containsKey(val)) {
                direction = colorMap.get(val);
            } else {
                direction = new Direction();
                colorMap.put(val,direction);
            }
            direction.left = Math.min(direction.left, j);
            direction.right = Math.max(direction.right, j);
            direction.top = Math.min(direction.top, i);
            direction.bottom = Math.max(direction.bottom, i);
        }
    }
    while (!isAllPrint(targetGrid)) {
        int value=-1;
        Set<Map.Entry<Integer, Direction>> entries = colorMap.entrySet();
        for (Map.Entry<Integer, Direction> entry : entries) {
            Integer key = entry.getKey();
            if (sameColorAndPrintMark(key, entry.getValue(), targetGrid)) {
                value=key;
                break;
            }
        }
        if (value == -1) {
            return false;
        } else {
            //剔除
            colorMap.remove(value);
        }
    }
    return true;
}

private boolean isAllPrint(int[][] targetGrid) {
    for (int i = 0; i < targetGrid.length; i++) {
        for (int j = 0; j < targetGrid[i].length; j++) {
            if (targetGrid[i][j]!=0) {
                return false;
            }
        }
    }
    return true;
}

private boolean sameColorAndPrintMark(Integer key, Direction direction, int[][] targetGrid) {
    for (int i = direction.top; i <= direction.bottom; i++) {
        for (int j = direction.left; j <= direction.right; j++) {
            if(targetGrid[i][j]!=0&&targetGrid[i][j]!=key)
                return false;
        }
    }
    for (int i = direction.top; i <= direction.bottom; i++) {
        for (int j = direction.left; j <= direction.right; j++) {
            targetGrid[i][j] = 0;
        }
    }
    return true;
}
复制代码

image-20210610170024397

4、总结

  • 此题比较有意思的是须要分层考虑打印问题!从外至内进行打印渲染。可是由于不肯定因素因此咱们一次渲染没法最终得出结果,因此须要咱们进行屡次渲染。调试

  • 可是须要多少次咱们也没法肯定,这时候咱们就一直渲染!可是不能一直渲染因此咱们每次渲染后须要对是否须要继续下去进行断定code

  • 固然笔者这里也不是一番风顺的,提交过程也是不断的试错调试。这里我只是想告诉读者们刷题须要不断努力,不要由于错误而放弃orm

这里点个赞、关个注呗!持续贡献原创文章!若是你以为那个算法有意思,下方告诉我,我去试试可否攻克!!!leetcode

相关文章
相关标签/搜索