匈牙利算法(最大独立集) - 骑士放置 - AcWing 378
给定一个 N*M 的棋盘,有一些格子禁止放棋子。
问棋盘上最多能放多少个不能互相攻击的骑士(国际象棋的“骑士”,类似于中国象棋的“马”,按照“日”字攻击,但没有中国象棋“别马腿”的规则)。
输入格式
第一行包含三个整数N,M,T,其中T表示禁止放置的格子的数量。
接下来T行每行包含两个整数x和y,表示位于第x行第y列的格子禁止放置,行列数从1开始。
输出格式
输出一个整数表示结果。
数据范围
1 ≤ N , M ≤ 100 1≤N,M≤100 1≤N,M≤100
输入样例:
2 3 0
输出样例:
4
分析:
最大独立集: 选 择 图 中 最 多 的 点 放 入 一 个 集 合 中 , 集 合 内 部 任 意 两 点 之 间 没 有 边 。 选择图中最多的点放入一个集合中,集合内部任意两点之间没有边。 选择图中最多的点放入一个集合中,集合内部任意两点之间没有边。
最大团: 选 择 图 中 最 多 的 点 加 入 一 个 集 合 中 , 集 合 内 部 任 意 两 点 之 间 有 边 。 即 找 出 一 个 点 数 最 多 的 完 全 图 。 选择图中最多的点加入一个集合中,集合内部任意两点之间有边。即找出一个点数最多的完全图。 选择图中最多的点加入一个集合中,集合内部任意两点之间有边。即找出一个点数最多的完全图。
在 二 分 图 中 , 求 最 大 独 立 集 , 在二分图中,求最大独立集, 在二分图中,求最大独立集,
等 价 于 去 掉 最 少 的 点 , 使 得 剩 下 的 点 相 互 之 间 都 不 连 通 。 等价于去掉最少的点,使得剩下的点相互之间都不连通。 等价于去掉最少的点,使得剩下的点相互之间都不连通。
要 使 得 去 掉 的 点 最 少 , 那 么 每 删 去 一 个 点 , 就 必 须 破 坏 一 条 边 。 要使得去掉的点最少,那么每删去一个点,就必须破坏一条边。 要使得去掉的点最少,那么每删去一个点,就必须破坏一条边。
等 价 于 找 到 最 小 点 覆 盖 。 等价于找到最小点覆盖。 等价于找到最小点覆盖。
等 价 于 求 最 大 匹 配 。 等价于求最大匹配。 等价于求最大匹配。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#define P pair<int,int>
#define x first
#define y second
using namespace std;
const int N=110;
int n,m,k;
bool g[N][N],st[N][N];
P match[N][N];
int dir[8][2]={{-1,-2},{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2}};
bool Find(int x,int y)
{
for(int i=0;i<8;i++)
{
int a=x+dir[i][0],b=y+dir[i][1];
if(a<1||a>n||b<1||b>m) continue;
if(st[a][b] || g[a][b]) continue;
st[a][b]=true;
P t=match[a][b];
if(t.x==0 || Find(t.x,t.y))
{
match[a][b]={x,y};
return true;
}
}
return false;
}
int main()
{
cin>>n>>m>>k;
for(int i=0;i<k;i++)
{
int a,b;
cin>>a>>b;
g[a][b]=true;
}
int res=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if((i+j)%2 && !g[i][j])
{
memset(st,false,sizeof st);
if(Find(i,j)) res++;
}
cout<<n*m-k-res<<endl;
return 0;
}