力扣 LCP 04. 覆盖

力扣 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值