leetcode lcp4 覆盖

244 阅读2分钟
/**
 * @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
}