372 棋盘覆盖(二分图的最大匹配-匈牙利算法)

1. 问题描述:

给定一个 N 行 N 列的棋盘,已知某些格子禁止放置。求最多能往棋盘上放多少块的长度为 2、宽度为 1 的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。

输入格式

第一行包含两个整数 N 和 t,其中 t 为禁止放置的格子的数量。接下来 t 行每行包含两个整数 x 和 y,表示位于第 x 行第 y 列的格子禁止放置,行列数从 1 开始。

输出格式

输出一个整数,表示结果。

数据范围

1 ≤ N ≤ 100,
0 ≤ t ≤ 100

输入样例:

8 0

输出样例:

32
来源:https://www.acwing.com/problem/content/description/374/

2. 思路分析:

首先看到题目比较容易想到的是状态压缩dp,但是由于数据规模比较大,2 ^ 100那么肯定会超时,这道题目其实可以使用二分图的最大匹配来解决,一般来说可以使用二分图来解决的题目都比较难看出来,这也是二分图比较难的一个地方,如果看出来了那么就比较简单了,代码还是比较简短的,不容易写错,我们可以将每一个格子看成是一个点,把与当前格子相邻的格子看成是另外一个点,相邻的两个点那么就可以构成一条边,因为需要使得放置的卡片数量最大,所以我们需要使得图中的边是最多的,抽象之后也即最多可以取出来多少条边使得卡片是不重叠的,等价于所有选出来的边是没有公共点的,也即找一个最大匹配,并且我们还需要看一下是否是二分图,如果是二分图才可以使用匈牙利算法找最大匹配,如果不是二分图那么匈牙利算法是不可以使用的,我们可以隔一个格子进行染色,那么最终染色是没有矛盾的,所以是二分图,那么就可以使用匈牙利算法来解决。这道题目由于是在二维平面中的,所以match数组要声明为二维,并且match中的每一个元素是其对应的匹配坐标,对于python语言可以将match中的每一个元素声明为元组类型,c++语言可以声明为pair类型,与一维的匈牙利算法是类似的,并且需要注意一个问题,由于枚举的是当前坐标的上下左右四个方向,方向其实是相对的,所以只需要枚举奇数格子或者偶数格子即可。

3. 代码如下:

from typing import List


class Solution:
    pos = [[0, -1], [0, 1], [1, 0], [-1, 0]]
    # 匈牙利算法查找(x, y)的匹配
    def find(self, x: int, y: int, n: int, match: List[List[tuple]], st: List[List[int]], g: List[List[int]]):
        # 枚举上下左右四个方向
        for i in range(4):
            a, b = x + self.pos[i][0], y + self.pos[i][1]
            if a <= 0 or a > n or b <= 0 or b > n or g[a][b] == 1 or st[a][b] == 1: continue
            # 标记已经访问
            st[a][b] = 1
            if match[a][b][0] == -1 or self.find(match[a][b][0], match[a][b][1], n, match, st, g):
                match[a][b] = (x, y)
                return True
        return False

    def process(self):
        n, m = map(int, input().split())
        g = [[0] * (n + 10) for i in range(n + 10)]
        for i in range(m):
            a, b = map(int, input().split())
            # 标记当前的位置是坏的
            g[a][b] = 1
        # match的每一个元素是一个元组
        match = [[(-1, -1)] * (n + 10) for i in range(n + 10)]
        # 因为在枚举的时候相邻两个格子的只是方向不一样所以只需要枚举奇数格子或者是偶数格子, 这样答案只算一遍即可
        res = 0
        for i in range(1, n + 1):
            for j in range(1, n + 1):
                # 当前的格子是奇数格子并且当前位置没有坏
                if (i + j) % 2 and g[i][j] != 1:
                    st = [[0] * (n + 10) for i in range(n + 10)]
                    if self.find(i, j, n, match, st, g): res += 1
        return res


if __name__ == "__main__":
    print(Solution().process())

你可能感兴趣的:(acwing-提高,算法,算法)