【vijos】【神读入】Knights
描述
在一个N*N的正方形棋盘上,放置了一些骑士。我们将棋盘的行用1开始的N个自然数标记,将列用’A’开始的N个大写英文字母标记。举个例子来说,一个标准的8*8的国际象棋棋盘的行标记为1..8,列标记为A..H,D3、H1分别表示棋盘上第3行第4列和第1行第8列的格子。
骑士是这样一类棋子。若一个骑士放置在格子(x, y)。那么格子(x-2, y-1), (x-2, y+1), (x-1, y-2), (x-1, y+2), (x+1, y-2), (x+1, y+2), (x+2, y-1), (x+2, y+1)如果在棋盘内的话,就都处于这个骑士的攻击范围内。
如果若干个骑士在棋盘上的一种放置方法能使得没有一个骑士处在其它骑士的攻击范围内,那么称为和谐的方案。现在给定一个棋盘,上面已经放置了M个骑士。你的任务是拿走尽可能少的骑士,使得剩余的骑士构成一个和谐的方案。
格式
输入格式
第一行,两个正整数N,M,分别表示棋盘的大小,和骑士的数目。
以下M行,每行一个字符串,描述一个骑士的坐标。
输出格式
输出一行,一个整数,表示至少拿走多少个骑士。
代码
妹的居然不停scanf(%c)读到换行符…
裸的二分图最大匹配,用匈牙利模板秒…
注意左右两个集合内部是绝对不能有矛盾的,根据观察,骑士走日字,两个相互矛盾的骑士的坐标差一定为3,所以肯定一奇一偶,坐标和同奇偶的肯定不可能相互矛盾,作为依据分为左右两个集合就可以啦。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 0x3f
#define maxn 1000
using namespace std;
struct Po{
int x,y;
}knight[maxn];
int n,m;
int w[maxn][maxn],match[maxn];
bool vis[maxn],pan[maxn];
bool find(int x){
for(int i=1;i<=m;i++) if(w[x][i]){
if(vis[i]) continue;
vis[i]=true;
if(match[i]==-1||find(match[i])){
match[i]=x;
return true;
}
}
return false;
}
int suan(){
int ans=0;
for(int i=1;i<=m;i++) if(pan[i]){
memset(vis,0,sizeof(vis));
if(find(i)) ans++;
}
return ans;
}
int main(){
//freopen("in.txt","r",stdin);
char ch; int in;
memset(match,-1,sizeof(match));
memset(pan,0,sizeof(pan));
scanf("%d%d",&n,&m); scanf("%c",&ch);
for(int i=1;i<=m;i++){
scanf("%c%d",&ch,&in);
knight[i].x=int(ch)-int('A')+1;
knight[i].y=in;
if((knight[i].x+knight[i].y)%2==0) pan[i]=true;
if(i!=m) scanf("%c",&ch);
}
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
if(pan[i]^pan[j]){
if(abs(knight[i].x-knight[j].x)==1&&abs(knight[i].y-knight[j].y)==2){
w[i][j]=true;
}
else if(abs(knight[i].x-knight[j].x)==2&&abs(knight[i].y-knight[j].y)==1){
w[i][j]=true;
}
}
printf("%d",suan());
return 0;
}