【逆向思考 】【拓扑排序】1591. 奇怪的打印机 II

简介: 【逆向思考 】【拓扑排序】1591. 奇怪的打印机 II

本文涉及的知识点

逆向思考 拓扑排序

LeetCode1591. 奇怪的打印机 II

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

每一次操作时,打印机会用同一种颜色打印一个矩形的形状,每次打印会覆盖矩形对应格子里原本的颜色。

一旦矩形根据上面的规则使用了一种颜色,那么 相同的颜色不能再被使用 。

给你一个初始没有颜色的 m x n 的矩形 targetGrid ,其中 targetGrid[row][col] 是位置 (row, col) 的颜色。

如果你能按照上述规则打印出矩形 targetGrid ,请你返回 true ,否则返回 false 。

示例 1:

输入:targetGrid = [[1,1,1,1],[1,2,2,1],[1,2,2,1],[1,1,1,1]]

输出:true

示例 2:

输入:targetGrid = [[1,1,1,1],[1,1,3,3],[1,1,3,4],[5,5,1,4]]

输出:true

示例 3:

输入:targetGrid = [[1,2,1],[2,1,2],[1,2,1]]

输出:false

解释:没有办法得到 targetGrid ,因为每一轮操作使用的颜色互不相同。

示例 4:

输入:targetGrid = [[1,1,1],[3,1,3]]

输出:false

提示:

m == targetGrid.length

n == targetGrid[i].length

1 <= m, n <= 60

1 <= targetGrid[row][col] <= 60

拓扑排序

给颜色x盖章,一定从最左上的x,改到最右下的x,否则会依赖。

逆向思考,从后向前思考。给x盖的时候,这个区域除了x,都已经处理。

用拓扑排序。

如果x所在的矩形包括y,此存在边边x → \rightarrow y

代码

核心代码

class CTopSort
{
public: 
  template <class T = vector<int> >
  void Init(const vector<T>& vNeiBo,bool bDirect = true )
  {
    const int iDelOutDeg = bDirect ? 0 : 1;
    m_c = vNeiBo.size();
    m_vBackNeiBo.resize(m_c);
    vector<int> vOutDeg(m_c);
    for (int cur = 0; cur < m_c; cur++)
    {
      vOutDeg[cur] = vNeiBo[cur].size();  
      for (const auto& next : vNeiBo[cur])
      {
        m_vBackNeiBo[next].emplace_back(cur);
      }
    }
    vector<bool> m_vHasDo(m_c);
    queue<int> que;
    for (int i = 0; i < m_c; i++)
    {
      if ( vOutDeg[i] <= iDelOutDeg)
      {
        m_vHasDo[i] = true;
        if (OnDo(i)) {
          que.emplace(i);
        }
      }
    }
    while (que.size())
    {
      const int cur = que.front();
      que.pop();
      for (const auto& next : m_vBackNeiBo[cur])
      {
        if (m_vHasDo[next] )
        {
          continue;
        }       
        vOutDeg[next]--;
        if (vOutDeg[next] <= iDelOutDeg )
        {
          m_vHasDo[next] = true;
          if (OnDo(next)) {
            que.emplace(next);
          }
        }
      }
    };
  }
  int m_c;
protected:
  virtual bool OnDo( int cur) = 0;
  vector<vector<int>> m_vBackNeiBo; 
  
};
class CMyTopSort : public CTopSort
{
public:
  int m_iCount = 0;
  virtual bool OnDo(int cur) override
  {
    m_iCount++;
    return true;
  }
};
class Solution {
public:
  bool isPrintable(const vector<vector<int>>& targetGrid) {
    vector<unordered_set<int>> vNeiBo(61);
    for (int x = 1; x <= 60; x++)
    {
      int l = 100, t = 100, right = -1, b = -1;
      for (int r = 0; r < targetGrid.size(); r++)
      {
        for (int c = 0; c < targetGrid.front().size(); c++)
        {
          if (targetGrid[r][c] == x)
          {
            l = min(l, c);
            right = max(right, c);
            t = min(t, r);
            b = max(b, r);
          }
        }
      }
      for (int r = t; r <= b; r++)
      {
        for (int c = l; c <= right; c++)
        {
          if (targetGrid[r][c] != x)
          {
            vNeiBo[x].emplace(targetGrid[r][c]);
          }
        }
      }
    }
    CMyTopSort topSort;
    topSort.Init(vNeiBo);
    return 61 == topSort.m_iCount;
  }
};

测试用例

template<class T, class T2>
void Assert(const T& t1, const T2& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{
  vector<vector<int>> targetGrid;
  {
    Solution sln;
    targetGrid = { {1,1,1,1},{1,2,2,1},{1,2,2,1},{1,1,1,1} };
    auto res = sln.isPrintable(targetGrid);
    Assert(true, res);
  }
  {
    Solution sln;
    targetGrid = { {1,1,1,1},{1,1,3,3},{1,1,3,4},{5,5,1,4} };
    auto res = sln.isPrintable(targetGrid);
    Assert(true, res);
  }
  {
    Solution sln;
    targetGrid = { {1,2,1},{2,1,2},{1,2,1} };
    auto res = sln.isPrintable(targetGrid);
    Assert(false, res);
  }
  {
    Solution sln;
    targetGrid = { {1,1,1},{3,1,3} };
    auto res = sln.isPrintable(targetGrid);
    Assert(false, res);
  }
}


扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。

https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程

https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关文章
|
2月前
|
C语言 索引
一堆数组程序
一堆数组程序
10 0
|
3月前
|
算法 测试技术 C++
【动态规划】【记忆化搜索】【C++算法】664. 奇怪的打印机
【动态规划】【记忆化搜索】【C++算法】664. 奇怪的打印机
|
4月前
(模拟)L1-019. 谁先倒(2016)
(模拟)L1-019. 谁先倒(2016)
22 1
|
5月前
|
SQL 算法 vr&ar
☆打卡算法☆LeetCode 182. 查找重复的电子邮箱 算法解析
☆打卡算法☆LeetCode 182. 查找重复的电子邮箱 算法解析
☆打卡算法☆LeetCode 182. 查找重复的电子邮箱 算法解析
|
5月前
|
存储 C语言
换硬币C语言(超详细分析!包会)
换硬币C语言(超详细分析!包会)
50 1
换硬币C语言(超详细分析!包会)
|
12月前
|
JSON NoSQL Redis
逆转时间,起死回生——程序报错崩溃后,如何倒回到崩溃的位置?
逆转时间,起死回生——程序报错崩溃后,如何倒回到崩溃的位置?
72 0
枚举时对数组操——三刷AcWing 95. 费解的开关
枚举时对数组操——三刷AcWing 95. 费解的开关
46 0
|
缓存 算法 Java
|
算法
【算法竞赛进阶指南】程序自动分析(并查集判冲突+离散化)
【算法竞赛进阶指南】程序自动分析(并查集判冲突+离散化)
94 0
|
算法 C++
【数独 1】不回溯,试试候选数法1ms高效解数独谜题-C++实现(上)
【数独 1】不回溯,试试候选数法1ms高效解数独谜题-C++实现(上)
108 0