/**
* @param {number} n
* @param {number} m
* @param {number[][]} broken
* @return {number}
* 这个问题的条件天然的把棋盘分成了一个二分图, 问题的要求就是要求
* 二分图的最大匹配中的边的数量
* 很明显可以用匈牙利算法。
* 匈牙利算法的流程是:
* 1.每次从另一个集合的第一个点开始尝试给本集合的当前点匹配
* 2.如果一个集合中的点在另一个集合中有点可以连就直接连
* 3.如果连不了, 那就把这个点分配给当前点,然后给这个点原先匹配的点分配一个新的点,
* 这就是匈牙利算法
*/
let neighbor = null
let from = null
let unVisible = null
let ln = 0
let lm = 0
let num = 0
let visited = null
var domino = function(n, m, broken) {
ln = n
lm = m
num = ln * lm
// 初始化neighbor
neighbor = new Array(num)
for(let i = 0; i < num; i++) neighbor[i] = new Array(num)
// 初始化from
from = new Array(num).fill(-1)
// 初始化已访问过的点
visited = new Array(num)
// 记录坏掉的点
unVisible = new Array(ln)
for(let i = 0; i < ln; i++) unVisible[i] = new Array(lm)
for(let b of broken) unVisible[b[0]][b[1]] = true
// 记录相邻的点
for(let i = 0; i < ln; i++) {
for(let j = 0; j < lm; j++) {
if(unVisible[i][j]) continue
let d = i * lm + j
if(((j + 1) < lm) && (!unVisible[i][j + 1])) {
neighbor[d][i * lm + j + 1] = 1
neighbor[i * lm + j + 1][d] = 1
}
if(((i + 1) < ln) && (!unVisible[i + 1][j])) {
neighbor[d][(i + 1) * lm + j] = 1
neighbor[(i + 1) * lm + j][d] = 1
}
}
}
return hungary()
};
function match(x) {
for(let i = 0; i < num; i++) {
let dx = (i / lm) | 0
let dy = i % lm
// 不相邻的点直接忽略
if(neighbor[x][i] !== 1) continue
// 坏掉的点直接忽略
if(unVisible[dx][dy]) continue
// 一次循环已访问的点直接忽略
if(!visited[i]) {
visited[i] = true
if((from[i] < 0) || match(from[i])) {
from[i] = x
return 1
}
}
}
return 0
}
function hungary() {
let ans = 0
for(let i = 0; i < num; i++) {
let dx = (i / lm) | 0
let dy = i % lm
if(unVisible[dx][dy]) continue
// 每轮循环初始化visited
for(let j = 0; j < num; j++) visited[j] = 0
if((dx + dy) & 1) {
ans += match(i)
}
}
return ans
}