Description
.
We are given a chess-board of size nn, from which some fields have been removed. The task is to determine the maximum number of knights that can be placed on the remaining fields of the board in such a way that none of them check each other.
一张大小为nn的国际象棋棋盘,上面有一些格子被拿走了,棋盘规模n不超过200。马的攻击方向如下图,其中S处为马位置,标有X的点为该马的攻击点。
Fig.1: A knight placed on the field S checks fields marked with x.
Write a program, that:
reads the description of a chess-board with some fields removed, from the input file kni.in,
determines the maximum number of knights that can be placed on the chess-board in such a way that none of them check each other,
writes the result to the output file kni.out.
你的任务是确定在这个棋盘上放置尽可能多的马,并使他们不互相攻击。
Input
The first line of the input file kni.in contains two integers n and m, separated by a single space, 1<=n<=200, 0<=m<n2; n is the chess-board size and m is the number of removed fields. Each of the following m lines contains two integers: x and y, separated by a single space, 1<=x,y<=n – these are the coordinates of the removed fields. The coordinates of the upper left corner of the board are (1,1), and of the bottom right are (n,n). The removed fields are not repeated in the file.
Output
The output file kni.out should contain one integer (in the first and only line of the file). It should be the maximum number of knights that can be placed on the given chess-board without checking each other.
Sample Input
3 2
1 1
3 3
Sample Output
5
解题思路
经典棋盘黑白染色
比如一个3x3的矩阵染色
如果我们将跳马图画出来,会发现白格一定会跳到黑格,黑格一定会跳到白格
将黑格和白格分成两个集合,黑格与能跳到的白格连边,建出一个二分图
一个格子的数对压缩成一个数,
(
i
,
j
)
−
>
(
i
−
1
)
∗
n
+
j
(i,j)->(i-1)*n+j
(i,j)−>(i−1)∗n+j
经过这神神奇奇,奇奇怪怪一大串后
我们可以得出,这题是做一个最大匹配,用答案 = 点数 - 空格 - 最大匹配数
Code
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int way[8][2] = {{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {2, -1}, {2, 1}, {1, -2}, {1, 2}};
struct DT{
int to, next;
}a[402000];
long long n, m, ans, use[40200], black[40200];
int v, num, x, y, head[40200], s[220][220], kong[220][220];
bool check (int x, int y){
return (x<=n&&x>0&&y<=n&&y>0);
}
void add (int x, int y){
a[++num].to = y, a[num].next = head[x], head[x] = num;
}
int find (int x){//最大匹配
for (int i = head[x]; i; i = a[i].next)
{
int t=a[i].to;
if (use[t]) continue;
use[t] = 1;
if(!black[t] || find(black[t]))
{
black[t] = x;
return 1;
}
}
return 0;
}
int main(){
scanf ("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
s[i][j] = (i - 1) * n + j;//压缩数对
for (int i = 1; i <= m; i++)
{
scanf ("%d%d", &x, &y);
kong[x][y] = 1;//标记空格
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
if(kong[i][j] || (i+j)%2) continue;//我们从黑格出发做
for (int k = 0; k < 8; k++)
{
int x = i + way[k][0], y = j + way[k][1];
if(check (x, y) && !kong[x][y])
add (s[i][j], s[x][y]);//黑格与能跳到的白格连边
}
}
for (int i = 1; i <= n*n; i++)
{
memset (use, 0, sizeof (use));
ans += find (i);//匈牙利
}
printf ("%lld", n * n - ans - m);
}