网格问题
最大权子矩形问题
给定一个n×m网格,每个网格上有一个整数,要求选择一个非空子矩形,要求子矩形中包含的数的总和最大。
我们可以暴力枚举子矩形的上下边界,现在的问题就是一个序列,要求求一段子序列使得总和最大,这是一个经典问题,可以O(n)求解。
整体时间复杂度为O(n2m)(我们可以旋转整个网格得到时间复杂度O(m2n))。
二维最大面积子矩形问题
给定一个矩形大厅,大厅中共有n根柱子,第i根柱子的坐标为(xi,yi),柱子很细,可以视作一个点。要求在大厅布置一个矩形舞台,要求舞台和柱子不能重合(允许落在边缘,但不允许落在内部),问舞台的最大面积。
提供一道题目。
可以发现舞台的边缘要么与大厅相接,要么与柱子相接。因此直接就给出了一个O(n5)的算法。
我们可以暴力枚举矩形的上边界,之后在考虑不同的下边界。当上下边界固定的时候,落在上下边界中的点的横坐标记作t1,t2,…,tk,要让面积最大,这时候很显然问题等价于找到距离最大的一对相邻的横坐标。这个可以O(n)计算。于是时间复杂度优化到了O(n3)。
下面我们考虑进一步优化,当下边界从j下移到j−1的时候,一些新的点会被加入考虑。实际上考虑加入一个新的点m,这个点左边横坐标为l,右边横坐标为r,其将原来某个连续的空间[l,r]切割为两部分[l,m]和[m,r]。这意味着相邻坐标最大距离有可能减小,减小不好处理,我们需要借助平衡树才好处理,这样的时间复杂度为O(n2log2n)。
虽然区间分裂不好处理,但是如果我们反过来处理,我们从下向上枚举下边界,这时候就变成了一开始区间全部分裂,之后不断删除点,区间合并。这时候我们就可以很简单的维护最大区间,时间复杂度为O(n2)。
全0矩形问题
题目1:给定一个n×m网格,每个网格上写着数字0或1。要求选择一个全0矩形,满足矩形面积最大。
如果我们给0替换为1,1替换为负无穷,问题就变成了最大权子矩形问题,我们就有了O(n2m)的算法。
我们可以暴力枚举子矩形的上边界i,并且对每个j=1,…,m,记fi(j)表示(i,j)上方的第一个1出现的纵坐标(如果不出现记做n+1)。我们知道一个最大面积子矩形必定不能再向上,左,右继续扩大,不能向上扩大是因为上方有1。我们分别考虑这个1出现的位置,其可能为(fi(j),j),对于给定1的位置,我们可以容易确定左右边界,左右边界可以通过递增队列来实现,因此对于给定i,我们可以O(m)算出相关的所有可能结果。
总的时间复杂度为O(nm)。
提供一道题目。
题目2:给定一个n×m网格,每个网格上写着数字0或1。要求对于1≤i≤n,1≤j≤m输出f(i,j),其中f(i,j)表示高为i宽度为j的全0矩形总数。
首先如果我们暴力枚举左上角顶点,可以得到O((nm)2)的算法。
考虑枚举上下边界,我们可以发现一列可以被选择,当且仅当在上下边界内这一列都是0。这时候我们的到一个01序列。我们需要统计不同宽度的连续0序列的数目。我们可以将01序列划分为若干个不可扩增的0序列,并记录它们的长度为w1,…,wk,记录C(i)表示i在w序列中出现的次数。记g(i)为长度为i的0序列数目,可以发现g(i)=g(i+1)+∑mj=iwj。这个我们可以通过后缀和可以O(m)求出。因此现在我们得到了O(n2m)的算法。
接下来我们进一步优化。考虑我们处理了上边界为U,下边界为D的情况,这时候我们将D增大1,考虑会发生什么。我们可以发现一些w中的元素被分裂成两个,这对应于C(i)的常数次加减操作。并且每一列在这个过程中都最多仅贡献一次,因此仅考虑这些操作实际上是O(m)级别的。但是还有一个问题,我们在枚举下边界的时候每一行都需要复制C,这显然是很大的开销,我们可以通过维护前缀和的技术来实现,这样我们只需要O(m)级别的修改即可。还有一个问题就是对应不同的上边界我们需要维护不同的前缀和,但是由于我们最后只需要算出C(i)的总和而已,因此我们把这些前缀和数组合并在一起,一起维护即可。这样对于每个固定的上边界,我们的时间复杂度为O(m)。最后跑一次我们提到的计算g的算法,算出所有的f(i,j)即可。总的时间复杂度为O(nm)。
提供一道题目。
题目3:
全0方形矩阵
一个网格,每个网格上面写着数字0或1。一个方形矩形是全0的,当且仅当矩形中只包含数字0。
题目1:给定一个n×m的0,1网格G,要求找到其中最大的全0方形矩形。如果无解,则输出0。
我们考虑以(x,y)为右下角的最大方形网格大小,记其为f(x,y),如果Gi,j=1,则答案为0,否则为min(f(x−1,y),f(x−1,y−1),f(x,y−1))+1。
证明可以通过上下界来证明。
时间复杂度为O(nm)。
题目2:给定一个n×m的0,1网格G。之后要求做k次修改,第i次修改将某个位置的0改成1。要求在每次修改后输出当前最大的全0方形矩形的大小。其中1≤n,m,k≤2×103。
提供一道题目。
首先增加1比较麻烦,我们逆向操作,先把所有操作执行完,之后逐步回退,这时候相当于把1改成0。
当删除一个1之后,考虑新的最大方形矩形的大小。如果增大,则最大方形矩形必定包含我们之前删除的点所在的行。
我们可以为每一列维护两个数组U,D,其中Ui,j表示从(i,j)向上移动第一个1的距离。同理Di,j表示从(i,j)向下移动第一个1的距离。可以发现每一次修改后,我们仅需要更新一列,时间复杂度为O(n)。
之后我们尝试max+1是否存在,其中max表示已知最大全0方形矩形的大小。之后我们我们可以用遍历可能的最大矩形的右边界,同时用两个最小队列维护该行U,D的信息。一次判断的时间复杂度为O(m)。
考虑到判断最多只会发生O(m+k)次,因此总的时间复杂度应该为O(nm+km)。
01网格异或问题
题目1:给定一个大小为n×m的01网格A。每次操作允许将某一行或某一列全部取反,问使得网格中所有元素变成0的最少操作次数,或者报告无解。
我们可以暴力枚举第一行是否需要取反,第一行确定,所有列是否需要翻转也同时确定了,同样的所有行也确定了。因总的时间复杂度为O(nm)。
同值连通块
同值连通块是指值相同且相邻的单元格并入同一个连通后得到。
题目1:给定一个大小为n×m的01网格A。找到其中最大的子矩形,满足其中所有同值连通块都是一个矩形形状。其中1≤n,m≤1000
如果某个子矩形R满足条件,那么上面条件等于R的所有2×2的子矩形的异或和都是0。我们可以先求出另外一个大小为n−1×m−1的网格B,其中Bi,j=⊕i≤x≤i+1,j≤y≤j+1Ax,y。可以发现一个最大的子矩形,对应与B中的一个最大的全0矩阵,并加上尾部和右边一行。这个问题实际上是最大全0矩形问题,可以O(nm)解决。
题目2:给定一个大小为n×m的01网格A。要求判断仅修改最多k个数的前提下,是否可以保证其中所有同值连通块都是一个矩形形状。其中1≤n,m≤200,0≤k≤10。
提供一道题目。
题目1已经提到过了A满足条件等于A的所有2×2的子矩形的异或和都是0。可以发现如果我们确定了第一行和第一列后,整个矩形都确定了,即确定两个序列X,Y,那么Ai,j=Xi⊕Yj。
由于k非常小,因此我们可以分情况讨论。
情况1:n≤k,这时候暴力枚举所有的可能的X即可,Y可以贪心选择。时间复杂度为O(nm2n)。
情况2:n>k,这时候至少有一行是不经过任何修改的。暴力枚举这一行,这一行一旦确定,Y也随之确定,之后其余行,就贪心选择使得这一行修改最少的方案。总的时间复杂度为O(n2m)。
网格转换问题
问题1:给定两个大小为n×m的网格A,B,给定k,要求每次选择A中连续的k个同行元素或同列元素,并修改一个任意值v。问是否可以通过一定的步骤,使得A转换为B。
提供一道题目。
首先我们可以将A和B都减去B,这时候A变成A−B,而B=0。我们现在希望做一些操作,使得A变成全0矩阵。如果A可以被转换,则称A是可零化的。
我们可以创建一个大小为k×k的新的网格C,其中对于任意的A中下标(i,j),我们将Ai,j加入到C_{i\pmod k,j\pmod k}中。我们实际上可以通过一些步骤将A的左上角的k\times k矩阵转换为C,同时其它部分转换为0,具体做法就是选择A_{r,i}到A_{r,i-1+k}之间所有元素增加1,选择A_{r,i+1}到A_{r,i+k}之间所有元素减少1,这等价于从A_{r,i+k}将一个1移动到A_{r,i}上。因此C能零化,则A也一定能零化。同理如果A能零化,那么将A中的操作,如果是选择行r,则在C中选择r\pmod k,如果选择列c,则在C中选择c\pmod k,修改的值不变,作用到C上去,此时C也同样能被零化。
因此A能被零化等价于C能被零化。最后考虑是否能零化C。这个问题等价于选择两个长度为k的向量X,Y,其中C_{i,j}=X_i+Y_j。我们可以枚举X_0,这时候就能确定Y,从而确定X。但是X_0可能性太多了,考虑X_0是一个可行解,那么X_0+t同样是一个可行解,因为我们只需要将X中所有元素都加上t,同时让Y中所有元素减少t即可得到一个新的可行解。故我们只要取X_0=0来测试即可。
时间复杂度为O(nm)。
问题2:给定两个大小为n\times m的网格A,B,给定h,w,要求每次选择A中大小为h\times w的子矩阵,将子矩阵中所有元素都修改一个任意值v。问是否可以通过一定的步骤,使得A转换为B。
记R_{i,j}=A_{i,j}+A_{i,j+w}+A_{i,j+2w}+\ldots,记C_{i,j}=A_{i,j}+A_{i+h,j}+A_{i+2h,j}+\ldots。可以发现每一次修改后,R_{i,1},\ldots,R_{i,w}任意两个元素的差值是不变的,同理C_{1,j},\ldots,C_{h,j}中任意两个元素的差值是不变的。简单记\delta R_{i,j}=R_{i,j}-R_{i,1},同理记\delta C_{i,j}=C_{i,j}-C_{1,j}。可以发现一个网格的(\delta R,\delta C)是唯一的,并且两个可以相互转换的网格必须拥有相同的(\delta R,\delta C)。并且如果两个网格拥有相同的(\delta R,\delta C),那么它们一定也是可以相互转换的,因为我们可以把左上角大小为n-(h-1)\times m-(w-1)的网格全部化零,此时剩下的元素是唯一确定的。
上面提供了一个O(n\times m)时间复杂度的算法来解决这个问题。
互异矩形问题
题目1:给定一个n\times m的矩形A,第i行第j列单元格包含一个整数A_{i,j},要求找到其中最大面积的子矩形,满足矩形覆盖的元素两两不同。其中1\leq n,m\leq 500,且1\leq A_{i,j}\leq 10^9。
提供一道题目。
最简单的做法是暴力枚举所有子矩形,这样时间复杂度为O(n^3m^3)。
可以稍微优化一些,固定左上角,考虑不同的右下角,这样可以优化到O(n^2m^2)。
现在我们固定上边界,递增下边界。记R(i)表示以第j列为左边界,右边界的值,很显然R(i)\leq R(i+1)。考虑这时候我们下移下边界对R(i)带来的影响。这时候R(i)可能会减小。如何我们对每个单元格(i,j)预处理left(i,j)和right(i,j),left(i,j)分别表示它左上角的所有值与它相同的单元格的最大y坐标,而right(i,j)则表示它的右上角的所有值与它相同的单元格的最小y坐标。这样我们下移下边界的时候,右边界的值就可以O(m)进行更新了。
现在的问题是如何维护left(i,j)和right(i,j),直接计算比较复杂,需要为每个值维护一个单调队列。我们可以考虑上移上边界对left(i,j)的影响,此时它只可能增大。这时候我们可以O(nm)维护新的left值,right值的维护类似。
总的时间复杂度为O(n^2m)。
最小和网格赋值方案
要求找到一个n\times m的网格G,其中第i行的所有元素的异或值为R_i,第j列的所有元素的异或值为C_j。且要求所有元素的和最小。其中1\leq n,m\leq 10^3,0\leq C_i,C_j\leq 10^9。如果无解,则报告无解,否则输出任意一组可行解。
考虑到每个比特可以独立考虑,因此我们可以拆比特独立处理。
下面我们仅需要考虑C_i,C_j只可能是0,1的情况。
很显然有解的前提是\oplus_{i=1}^nR_i=\oplus_{i=1}^mC_i。之后我们会证明这个条件同时是充分的。容易发现和的一个下界为\max(\sum_{i=1}^n R_i,\sum_{j=1}^mC_j)。下面我们通过构造法在满足有解前提下一定可以达到下界。
不妨认为A=\sum_{i=1}^n R_i\geq \sum_{j=1}^mC_j=B,这时候A,B拥有相同的奇偶性,并且R_1,\ldots,R_A=1,C_1,\ldots,C_B=1。那么我们只需要设置G_{1,1},G_{2,2},\ldots,G_{B,B},G_{B,B+1},G_{B,B+2},\ldots,G_{B,A}为1即可。
上面我们提供了一个O(nm\log_2M)的算法。