Square Free-Google Codejam 2021 Round 3第二题
We have a matrix of square cells with R rows and C columns. We need to draw a diagonal in each cell. Exactly one of two possible diagonals must be drawn in each cell: the forward slash diagonal, which connects the bottom-left and the top-right corners of the cell, or the backslash diagonal, which connects the top-left and the bottom-right corners of the cell.
For each row and column, we want to draw a specific number of diagonals of each type. Also, after all the diagonals are drawn, the matrix should be square free. That is, there should be no squares formed using the diagonals we added.
For example, suppose we have a matrix with 4 rows and 4 columns. The number next to each row is the exact number of forward slash diagonals there must be in that row. The number below each column is the exact number of forward slash diagonals there must be in that column.
The first two matrices are not square free, while the third matrix is. In the first matrix, there is a square of side-length 2 diagonals with its vertices in the middle of each side of the matrix. In the second matrix, there is a square of side-length 1 diagonal drawn in the bottom-right corner. In the third matrix, there is no square. The third matrix would then be a valid drawing according to all the rules.
Given the size of the matrix and the exact number of forward slash diagonals that must be drawn in each row and column, produce any square free matrix that satisfies the row and column constraints, or say that one does not exist.
题目大意
我们要把一个 R × C ( 1 ≤ R , C ≤ 20 ) R\times C(1\leq R,C\leq 20) R×C(1≤R,C≤20)的矩形方阵中每一个方格中填上对角线,每一行每一列都预先给定了最终方案向右的对角线的个数,同时,我们需要保证我们填上的对角线不会形成正方形。如上面三个图所示,数字代表该行(列)所需向右对角线个数,只有第三个方案满足不存在正方形的要求。给定矩阵的大小及每行每列所需向右对角线的个数,判断是否存在合法方案;如果有合法方案,任意输出一个合法方案。
思路解析
这道题可以说是一个缝合怪了,不过每个部分的模型还是相当经典的。为了方便我们简称向右对角线为r,向左对角线个数为l。
首先,每行每列的对角线个数要求是经典的网络流模型。每行每列可以用一个结点表示,然后第i行j列的方格看做是第i行结点向第j列结点连一条容量为1的边,行结点向源点连容量为该行要求r个数的边,列结点向汇点连容量为该列要求r个数的边。这样,如果最大流是满流,那么我们就找到了一种方案。
考虑怎么处理正方形的问题。观察下图:
我们发现要破坏一个正方形,我们可以把标红的四条边全部反向,这样每行每列的r个数不变,当前的正方形却消失了。但是这个操作可能产生新的正方形,我们怎么保证这个操作一定只会进行有限次呢?
这里经典的想法是:定义r>l,那么上述翻转操作实际上是一个把方案的字典序变小的过程,因为我们把大的r换到了小的l后面。因此,我们只需要重复上述操作直到找不到正方形为止就可以了。这个操作只会进行有限次,因为字典序是有最小值的。
当然,现在我们观察到了字典序的问题,我们也可以选择在一开始就直接找最小字典序的方案。我们考虑使用费用流,给越前面的方格代表的边更小的费用,这样最小费用最大流可以找出最小字典序方案。不过要找严格字典序最小需要把每个方格设置为2的次方,一下就指数爆炸了。考虑到我们只需要保证如下操作能让费用严格变小:一开始 a i m a_{im} aim向右, a i n a_{in} ain向左, a j m