力扣2132. 用邮票贴满网格图
这一题的大意是给出一个网格图,是一个mn的二进制矩阵,为0表示为空,为1表示为占据。现在往里面贴邮票,贴邮票的规则是:
覆盖所有空格子。
不覆盖任何被占据的格子。
我们可以放入任意数目的邮票。
邮票可以相互有重叠部分。
邮票不允许旋转 。
邮票必须完全在矩阵内 。
邮票的长宽为:stampHeight x stampWidth
现在的目标是试图往这个格子里面贴满邮票,如果可以贴满,就返回true,否则返回false。
我们根据规则,可以知道要想贴满,我们只能从一个个符合邮票的子矩阵中试,看能不能往里面贴,也即在这个子矩阵中有没有被占据的格子,如果可以,就往里面贴邮票。
根据题目上给出的数据范围,我们知道O(mn)<=2*10^ 5 ,m,n <=2 * 10^5
因此我们只能用双重for循环,对矩阵进行遍历一次来实现目标。
暴力不行,这一题很明显可以看出需要前缀和+差分的优化,
检查子矩阵中有没有被占据的格子,可以用二维前缀和计算子矩阵的面积来表示,因为是二进制矩阵,只需要子矩阵的面积==0就表示该子矩阵是可以被贴邮票的
贴邮票的过程,我们可以用差分数组来表示,这样完美的只需要n * m的时间复杂度
for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+grid[i-1][j-1];}}for(int i=0;i+stampHeight-1<n;i++){for(int j=0;j+stampWidth-1<m;j++){//这里应该求子矩阵的面积if(sum[i+stampHeight][j+stampWidth]-sum[i+stampHeight][j]-sum[i][j+stampWidth]+sum[i][j]==0){//说明可以放邮票d[i+1][j+1]+=1;d[i+stampHeight+1][j+1]-=1;d[i+1][j+stampWidth+1]-=1;d[i+stampHeight+1][j+stampWidth+1]+=1;}}}
然后我们只需要对差分数组进行求一个前缀和即可得到贴上邮票后的矩阵的各个值,
我们只需要表示出每一个点的差分数组的前缀和的值不大于0(说明没有被贴邮票)并且该点原本的二进制矩阵的值也为0(说明该点也没有被占据),那么就说明该点既没有被贴邮票,也没有被占据,就返回false。反之返回true
时间复杂度为O(n*m)
注意:1.在计算子矩阵的时候要注意边界条件,实际上这一题也可以参考:
力扣1895. 最大的幻方
1895只运用了二维前缀和,但我觉得思想实际上还是挺像的,本题多用了二维差分。应该还有类似的题目,我做的题目比较少。
2.差分数组的前缀和数组,并不是表示的是面积,而是每一个点经过差分后的值,因为我总把差分数组的前缀和数组用sum表示,导致曲解了它的意思。
3.这种子矩阵的题要多总结,套路实际上是一样的。