骑士放置 (匈牙利算法 最大独立集)

这篇博客讨论了如何在N*M的棋盘上,避开禁止的格子,放置尽可能多的互不攻击的骑士。通过将棋盘上的点划分为奇偶两类,并利用二分图的性质,我们可以找到最大独立集,即最大数量的非攻击骑士。文章提供了具体的思路和参考代码,包括如何构建图,以及通过最大匹配数计算最大独立集。最后,算法的复杂度分析和实际应用价值也被提及。
摘要由CSDN通过智能技术生成

描述

给定一个 N*M 的棋盘,有一些格子禁止放棋子。问棋盘上最多能放多少个不能互相攻击的骑士(国际象棋的“骑士”,类似于中国象棋的“马”,按照“日”字攻击,但没有中国象棋“别马腿”的规则)。N, M<=100。

输入

第一行为n,m,t(表示有t个禁止的格子) 第二行到t+1行为x,y,分别表示禁止格子所在的位置,x为第x行,y为第y列,行列编号从1开始。

输出

一个整数,表示最多能放多少个骑士。

样例输入

2 3 0

样例输出

4

思路:

1.任意点(x1,y1),走日后,(x2,y2) 一定会从奇数变成偶数,偶数变成奇数,因此可以 划分奇数点和偶数点,显然可以用二分图来解决

2.要求多少个互不攻击的马,可以把每个格子看作一个点,在可以互相攻击的点间建立边, 二分图查找最大匹配数(即最小顶点数后),求得最大独立集即为最后结果。

3.最后的结果:最大独立集=总点数-最大匹配数-禁止点数

参考代码,详见注释:

#include <iostream>
#include <vector>
#include <string.h>
#include <stdio.h>
#define LL long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int MOD = 1e7+7;
const int INF = 0x3f3f3f3f;
const int N = 101;
int n,m,t; 
bool ma[N][N]; //存储图信息 
bool vis[N][N]; //是否访问过该点 
pair<int,int> p[N][N];//存放奇数点图对应的偶数点 
int dir[8][2]={{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};  
/*
最小顶点覆盖 = 最大匹配数,而最大独立集 = 顶点数 - 最小顶点覆盖 
1.任意点(x1,y1),走日后,(x2,y2) 一定会从奇数变成偶数,偶数变成奇数,因此可以
划分奇数点和偶数点,显然可以用二分图来解决 
2.要求多少个互不攻击的马,可以把每个格子看作一个点,在可以互相攻击的点间建立边,
二分图查找最大匹配数(即最小顶点数后),求得最大独立集即为最后结果。 
3.最后的结果:最大独立集=总点数-最大匹配数-禁止点数 
*/
bool get(int x,int y)
{
	int nx,ny,cx,cy;
	for(int i=0;i<8;i++)
	{ 
		//跳跃的下个点,一定为奇数点 
		nx=x+dir[i][0];
		ny=y+dir[i][1];
		//1.越界判断    2.禁止点   3.访问过的点 
		if(nx<1 || nx>n || ny<1 || ny>m || ma[nx][ny] || vis[nx][ny])continue;

		vis[nx][ny]=1; 

		cx=p[nx][ny].first;
		cy=p[nx][ny].second;
		//1.若当前奇数点没有对应的偶数点  2.或者当前偶数点能有其他的奇数点可达,则让给他 
		if( (cx==0 && cy==0) || get(cx,cy) )
		{
			p[nx][ny]={x,y};
			return true;
		}
	}
	return false;
 } 
int main()
{
	IO;
	int x,y;
	cin>>n>>m>>t;
	for(int i=0;i<t;i++)
	{
		cin>>x>>y;
		ma[x][y]=1;
	}
	int ans=0;
	
	//枚举偶数点 
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			//1.禁止点  2.奇数点 。 跳过枚举 
			if(ma[i][j] || ((i+j)&1) ) continue;
			memset(vis,0,sizeof(vis));
			if(get(i,j))ans++;
		}
	}
	//最大独立集=总点数-最大匹配数-禁止点数 
	cout<<n*m-ans-t;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值