题目
思路
- 匈牙利算法:匹配、最大匹配、匹配点、增广路径==》针对的是二分图算法学习笔记(5):匈牙利算法 - 知乎 (zhihu.com)
- 匹配:一组边,每两条边之间没有公共点;最大匹配指的是边数最多的一组匹配
- 增广路径:从非匹配点走,先走非匹配边,再走匹配边->非匹配边->匹配边......->非匹配点
- 重要结论:一个匹配是最大匹配等价于其不存在增广路径
- 把所有可以放置的格子当成一个点,相邻可以放置的点连成一条边,最多取多少条边,使得所有选出来的边没有公共点==》找一个最大匹配;
- 图是不是二分图(匈牙利算法只有在二分图上才能使用):能不能给一个图进行二染色,使得每条边的两个点是两种颜色==》所有格子分成(下标和)奇数和偶数格:奇点和偶点
- xmuoj | 二分图试炼之棋盘覆盖(附加题)
代码
贴一段C++的代码,Python代码会超时
#include <cstring>
#include <iostream>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
int n, m;
PII match[N][N];
bool g[N][N], st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
// dfs
bool find(int x, int y)
{
for (int i = 0; i < 4; i ++ )//枚举邻点
{
int a = x + dx[i], b = y + dy[i];
if (a && a <= n && b && b <= n && !g[a][b] && !st[a][b])//不是坏点 没遍历过
{
// 则男[x,y] 和 女[a,b]能够配对
st[a][b] = true;
PII t = match[a][b];//
//1 t.x==-1说明女[a,b]还没和其他人配对 则男[x,y]和女[a,b]可以直接配对
//2 女[a,b]已经有人配对,但和女[a,b]配对的男t还有其他选项
// 男t放弃和女[a,b]配对 让女[a,b]给男[x,y]配对(我感动了)
if (t.x == -1 || find(t.x, t.y))
{
match[a][b] = {x, y};
return true;
}
}
}
return false;
}
int main()
{
cin >> n >> m;
while(m--)
{
int x,y;
cin >> x >> y;
g[x][y] = true;
}
memset(match,-1,sizeof match);
int res = 0;
// 枚举所有和为奇数的点
for(int i=1;i<=n;i++)
{
for(int j = 1;j<=n;j++)
{
if((i+j)%2 && !g[i][j])
{
memset(st,0,sizeof st);//每次都需要清空st数组,因为匹配好的一对可能会有下家
if(find(i,j))res++;//如果[i,j]能配对
}
}
}
cout << res << endl;
return 0;
}
N = 110
dx = [-1, 0, 1, 0] # 定义 x 方向上的四个可能移动步长
dy = [0, 1, 0, -1] # 定义 y 方向上的四个可能移动步长
match = [[(-1, -1)] * N for _ in range(N)] # 存储配对关系的数组,初始化为 (-1, -1)
g = [[False] * N for _ in range(N)] # 存储坏点的数组,初始化为 False
st = [[False] * N for _ in range(N)] # 记录搜索状态的数组,初始化为 False
# 定义深度优先搜索函数
def find(x, y):
for i in range(4): # 枚举四个方向
a, b = x + dx[i], y + dy[i] # 计算移动后的坐标
if 0 < a <= n and 0 < b <= n and not g[a][b] and not st[a][b]: # 检查新坐标是否在合法范围内且未被访问过且不是坏点
st[a][b] = True # 标记新坐标为已访问
t_x, t_y = match[a][b] # 获取与新坐标配对的点的坐标
if t_x == -1 or find(t_x, t_y): # 如果该点未配对或可以重新配对
match[a][b] = (x, y) # 更新配对关系
return True # 返回成功
return False # 如果四个方向都无法配对,则返回失败
n, m = map(int, input().split()) # 输入 n 和 m
for _ in range(m): # 输入坏点的坐标并标记为坏点
x, y = map(int, input().split())
g[x][y] = True
res = 0 # 初始化配对数量
for i in range(1, n + 1): # 遍历所有可能的点
for j in range(1, n + 1):
if (i + j) % 2 and not g[i][j]: # 如果点的坐标和为奇数且不是坏点
for row in range(N): # 重置搜索状态数组
for col in range(N):
st[row][col] = False
if find(i, j): # 进行深度优先搜索,如果可以配对
res += 1 # 增加配对数量
print(res) # 输出配对数量