[貪心算法]套娃式覆蓋矩陣解決數字排列打印機

這是我參與更文挑戰的第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

相關文章
相關標籤/搜索