力扣 LCP 04. 覆盖
二分图+匈牙利算法+C语言实现
构建二分图:利用棋盘格子所在行列坐标之和的奇偶性,把格子分为两类,和为偶数的为偶数格,和为奇数的为奇数格(坏掉的格子都剔除掉),构成两个点集;每个格子跟自己上下左右的邻居格子都能构成一条边,代表能放骨牌(当然棋盘边上的格子、角点上的格子和周围有坏格子的格子除外,具体问题具体分析,多判断几下就好),从而构成边集。
匈牙利算法:简而言之就是,先到先得,能让就让,具体参考这篇博客
注意:以下代码注释中的“偶数格”和“奇数格”中的奇/偶分别代表格子所在行列坐标的和的奇/偶,设行坐标为i,列坐标为j,若(i + j)为偶数,则这个格子为偶数格;若(i + j)为奇数,则这个格子为奇数格。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N_MAX 8
#define M_MAX 8
#define GRID_NUM 64 //最大整个棋盘的格子数 = N_MAX * M_MAX = 8*8 = 64
int edge[GRID_NUM][GRID_NUM]; //边集
int pointEven[GRID_NUM] = {0}; //点集(存放没有坏的偶数格子的编号)
int pointOdd[GRID_NUM] = {0}; //点集(存放没有坏的奇数格子的编号)
int match[GRID_NUM] = {0}; //奇偶格匹配结果(骨牌能够摆放的方式)
int used[GRID_NUM] = {0};
int delta[] = {-1, 0, 1, 0, -1};//方向
int iEven = 0; //偶数格子点集的下标
int iOdd = 0; //奇数格子点集的下标
//匈牙利算法
int find(int n)
{
//printf("开始为偶数格%d寻找匹配\n", n);
for (int i = 0; i < iOdd; ++i)
{
//存在连接关系,且未被访问
if (edge[n][pointOdd[i]] != 0 && !used[pointOdd[i]])
{
used[pointOdd[i]] = 1; //置访问标记
//未被匹配,或能腾出位置
if (match[pointOdd[i]] == -1 || find(match[pointOdd[i]]))
{
match[pointOdd[i]] = n;
//printf("本轮寻找结果:偶%d -> 奇%d\n", n, pointOdd[i]);
return 1;
}
}
}
//printf("本轮寻找结果:偶数点%d无匹配\n", n);
return 0;
}
int domino(int n, int m, int** broken, int brokenSize, int* brokenColSize){
iEven = 0; //偶数格子点集的下标
iOdd = 0; //奇数格子点集的下标
int* board = (int*)malloc(n*m*sizeof(int)); //棋盘(好格子:0;坏格子:1)
memset(board, 0, n*m*sizeof(int));
//标记棋盘上所有坏格子
for (int i = 0; i < brokenSize; ++i)
{
*(board + (broken[i][0] * m + broken[i][1])) = 1;
}
memset(edge, 0, sizeof(edge));
memset(match, -1, sizeof(match));
//遍历棋盘上每一个点,赋编号,并与四周点匹配连线
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
//如果点(i, j)是坏格子,则不编号,直接跳过
if (*(board + i*m + j) == 1)
{
continue;
}
//编号
int id = i*m + j;
if ((i + j) % 2 == 0)
{
pointEven[iEven++] = id;
}
else
{
pointOdd[iOdd++] = id;
}
//四个方向判断连线(上、右、下、左)
for (int k = 0; k < 4; ++k)
{
int y = i + delta[k];
int x = j + delta[k+1];
//如果上/下/左/右奇数点(x, y)在棋盘内,且这个点不是坏点,就与当前偶数点(i, j)连线。表示可以放牌
if (x >= 0 && y >= 0 && x < m && y < n && *(board + y*m + x) == 0)
{
edge[i*m + j][y*m + x] = 1;
}
}
}
}
//依次为偶数格匹配奇数格
int res = 0;
for (int i = 0; i < iEven; ++i)
{
memset(used, 0, sizeof(int)*GRID_NUM);
res += find(pointEven[i]);
}
free(board);
return res;
}