[Balic2001]棋盘上的骑士
时间限制: 10 Sec 内存限制: 64 MB
题目描述
一个N*N的棋盘上,有一些小方格被拿走了,不能放置骑士,其它位置可以放。现要在棋盘上放若干骑士,要求任一个骑士都不能在其他骑士的攻击点上。请算出棋盘上最多能有几个骑士。骑士攻击范围如图所示(S是骑士的位置,X表示马的攻击点)
输入
第一行包含2个整数n和m,用单个的空格分开,1<=n<=200 , 0<=m < 40000;n 是国际象棋棋盘的大小,m是被拿走的格子数。 下面m行每行包含 2 个整数:x和y,用单个的空格分开,1<=x,y<=n,这些是被拿走的格子的坐标。 棋盘的左上角的坐标是(1,1),右下角是(n,n)。拿走的格子没有重复的。
输出
一个整数,它应该是能放在国际象棋棋盘上的互不攻击对方的马的最大的数量。
样例输入
3 2 1 1 3 3
样例输出
5
一道很坑的匈牙利匹配题,坑在搜索顺序上
首先建图,将下标奇偶性相同的格子看做一部,余下的看做另一部,根据象棋规则,必有X部向Y部连边的是非法状态(即两个位置不共存)
所以检查冲突匹配数,剩下的为答案
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int Max = 205;
struct node{
int v, nxt;
}edge[Max * Max << 3];
int N, T, cnt;
int fir[Max * Max], match[Max * Max];
int dd[8][2] = {{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {2, -1}, {2, 1}};
bool vis[Max * Max], P[Max][Max];
void addedge(int a, int b){edge[++ cnt].v = b, edge[cnt].nxt = fir[a], fir[a] = cnt;}
bool inarea(int a, int b){
if(a < 1 || b < 1 || a > N || b > N || P[a][b]) return 0;
return 1;
}
bool Hungary(int i){
for(int j = fir[i]; j; j = edge[j].nxt) if(!vis[edge[j].v]){
vis[edge[j].v] = 1;
if(! match[edge[j].v] || Hungary(match[edge[j].v])){
match[edge[j].v] = i;
return 1;
}
}
return 0;
}
int main(){
cnt = 1;
scanf("%d%d", &N,&T);
int Ans = N * N - T;
int x, y;
while(T --){
scanf("%d%d", &x, &y);
P[x][y] = 1;
}
for(int i = 1;i <= N; ++ i)
for(int j = 1; j <= N; ++ j)
if(! P[i][j] && (i & 1) == (j & 1)){
int p1 = (i - 1) * N + j;
for(int k = 0; k < 8; ++ k){
int xx = i + dd[k][0], yy = j + dd[k][1];
if(inarea(xx, yy)){
int p2 = (xx - 1) * N + yy;
addedge(p1, p2);
}
}
}
for(int i = 1; i <= N; ++ i)
for(int j = 1; j <= N; ++ j)
if(! P[i][j] && (i & 1) == (j & 1)){
memset(vis, 0, sizeof(vis));
Ans -= Hungary((i - 1) * N + j);
}
printf("%d\n", Ans);
return 0;
}