给定一个 N*M 的棋盘,有一些格子禁止放棋子。
问棋盘上最多能放多少个不能互相攻击的骑士(国际象棋的“骑士”,类似于中国象棋的“马”,按照“日”字攻击,但没有中国象棋“别马腿”的规则)。
输入格式
第一行包含三个整数N,M,T,其中T表示禁止放置的格子的数量。
接下来T行每行包含两个整数x和y,表示位于第x行第y列的格子禁止放置,行列数从1开始。
输出格式
输出一个整数表示结果。
思路:建图的思路还是很巧妙的,这个题目是我二分图最大独立集的第一个题目吧,解决了我的一个疑惑吧,
一开始还想着‘日’字式建图,后来发现好像没什么意义,后来想到了之前做过的一道题目,就是把这个图的行加列的奇偶来进行分图,想到这里,走了一下‘日’字,发现正好是两个集合之间相连,那么也就正好适合这个题目,
首先我们把图的行和列的奇偶来进行分图,然后就是判断日字的走法,能够走到的就建边。
最后我们知道最大独立集=点数-(最小点覆盖)最大匹配就行了,记得把T个坑点给去掉,
PS:一开始没理解为什么是减去最大匹配,为什么不是减去二倍的最大匹配,因为我找到的是边,不应该是一个边两个点吗?后来听学长说起来应该是认为每一个最大匹配边中去掉一个就组不成最大的匹配结果也就是独立集了
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=110*110+10;
const int ji=101;
int dx[]={1,-1,1,-1,2,-2,2,-2};
int dy[]={2,-2,-2,2,1,-1,-1,1};
int keng[110][110];
int pre[maxn],vis[maxn];
int n,m,t;
vector<int >G[maxn];
bool DFS(int u)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(vis[v]) continue;
vis[v]=1;
if(pre[v]==0||DFS(pre[v]))
{
pre[v]=u;
return true;
}
}
return false;
}
void AddEdge(int x,int y,int id)
{
for(int i=0;i<8;i++)
{
int nx=x+dx[i];
int ny=y+dy[i];
if(nx<1||nx>n||ny<1||ny>m) continue;
if(keng[nx][ny])continue;
int id1=nx*ji+ny;
G[id].push_back(id1);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&t);
for(int i=1;i<=t;i++)
{
int x,y;
scanf("%d%d",&x,&y);
keng[x][y]=1;
}
int id=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(keng[i][j]) continue;
if((i+j)%2==0)
{
id++;
AddEdge(i,j,id);
}
}
int ans=0;
for(int i=1;i<=id;i++)
{
memset(vis,0,sizeof(vis));
if(DFS(i)) ans++;
}
//cout<<ans<<endl;
printf("%d\n",n*m-ans-t);
}