前置知识
P2825 [HEOI2016/TJOI2016]游戏
注:本题做法与上题做法类似。
用到的算法:
- 图论——二分图——匈牙利算法(最大匹配)
- 建边——链式前向星。
解题思路
题目要求输出在题目给定的棋盘上能放最多的bishop(象)。
因为象的行走路线为对角线(斜线),不好写,所以我们将整个棋盘顺时针旋转45°(如图)。
由图可以看出,原先棋盘的对角线在顺时针旋转45°后变成了行和列。
由此,我们考虑行列匹配。
一、输入
这里有一个巨坑点(作者这个蒟蒻被这个东西坑了一周)。
输入原棋盘时,建议一行一行的输入并且输入整个棋盘后再进行操作,否则有可能“听取WA声一片”。
二、存储旋转后的棋盘
我们建立一个结构体 n o d e node node 。
struct node{
int xx,yy;
int can;
}a[2200][2200];
a
[
i
]
[
j
]
.
x
x
a[i][j].xx
a[i][j].xx 表示在原棋盘中
i
i
i 行
j
j
j 列的格子在旋转后位于
x
x
xx
xx 行。
a
[
i
]
[
j
]
.
y
y
a[i][j].yy
a[i][j].yy 表示旋转后位于
y
y
yy
yy 列。
c a n can can 的值分三种:
-
c
a
n
can
can = 0
该点可以放置象,且不会攻击到棋盘上原有棋子。 -
c
a
n
can
can = 1
该点已有棋子,不可放置象,会阻挡除马以外的攻击范围。 -
c
a
n
can
can = 2
该点未有棋子,但不可放置象,否则会攻击原有棋子或被原有棋子攻击,但不阻挡任何棋子的攻击范围。
接下来,我们看具体如何操作。
1. 旋转后的格子位置
我们假设
n
n
n = 4 ,
m
m
m = 5。
先观察原棋盘格子的位置。
再看旋转后格子的位置。
不难发现,棋盘旋转45°后,原位置为
(
i
,
j
)
(i,j)
(i,j) 的格旋转到了
(
i
+
j
−
1
,
j
+
n
−
i
)
(i+j-1,j+n-i)
(i+j−1,j+n−i) 的位置。
2.标记攻击范围
国际象棋中一共有 6 种棋子:
K
– king(国王),Q
– queen(皇后),B
– bishop(象,教主),N
– knight(马、骑士),R
– rook(车),P
– pawn(兵)。
在原棋盘上,棋子的攻击范围分别如下:
我们将棋子所在格子的
c
a
n
can
can 标记为
1
1
1 , 将攻击范围标记为
2
2
2 。
注意:除马(knight)以外,其他棋子的攻击范围均会被棋子阻挡。
如上图,车(R)的攻击范围被 兵(P) 阻挡,无法攻击到绿色格子。
你以为这道题的思路到此就结束了?不,还没有。
再细读题目,发现题目有这样一句话:
你需要避免的有 2 种情况: 你摆放的 bishop 之间的互相攻击以及你摆放的 bishop 与预先摆放好的棋子之间的互相攻击。
所以我们不仅要标记棋子原有的攻击范围,还要再加上一个象的攻击范围。
三、二分图最大匹配
在【一】里讲过,旋转45°后,象的攻击范围由对角线变为行和列。由此,我们可以行列匹配。
我们开两个数组
r
r
r 和
c
c
c 。
r
r
r 数组存储象在新棋盘的水平方向上的每一个不同的攻击范围
c
c
c 数组存储象在新棋盘的竖直方向上的每一个不同的攻击范围
下面举个简单的例子来进一步理解。
红色点为
N
N
N 的攻击范围
我们设新棋盘的水平方向上不同的攻击范围的标记为
l
e
n
r
lenr
lenr 。
- l e n r lenr lenr 每过一行需要加1,因为象在新棋盘上水平方向只能攻击到一行
- 如果遇到棋子, l e n r lenr lenr 也需要加1,因为棋子能阻挡象的攻击范围
则完成标记后的
r
r
r 数组如下:
标记
c
c
c 数组时同理,不作赘述。
下图为标记完成的
c
c
c 数组。
标记完成后,遍历新棋盘上的每一个点,若该点
(
i
,
j
)
(i,j)
(i,j) 可以放置象,则建立边连接
r
i
,
j
r_{i,j}
ri,j 与
c
i
,
j
c_{i,j}
ci,j (连接在该位置的象所能攻击到的两个方向的攻击范围编号)。
最后用匈牙利算法求最大匹配,就顺利切下这道题啦 !
下面结合代码来理解吧!
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
int head[1050000],to[1050000],ne[1050000],id;
int vis[20200],d[20200],n,m,maxx,ans;
int mp[2200][2200],r[2200][2200],c[2200][2200];
char s[1050][1050];
struct node{
int xx,yy;
int can;
}a[2200][2200];
void B(int x,int y){
for(int i=x-1,j=y-1;i>=1&&y>=1;i--,j--){
if(s[i][j]!='.') break;
a[i][j].can = 2;
}
for(int i=x+1,j=y+1;i<=n&&j<=m;i++,j++){
if(s[i][j]!='.') break;
a[i][j].can = 2;
}
for(int i=x+1,j=y-1;i<=n&&j>=1;i++,j--){
if(s[i][j]!='.') break;
a[i][j].can = 2;
}
for(int i=x-1,j=y+1;i>=1&&j<=m;i--,j++){
if(s[i][j]!='.') break;
a[i][j].can = 2;
}
}
void K(int x,int y){
int kx[8] = {-1,-1,-1,0,1,1,1,0};
int ky[8] = {-1,0,1,1,1,0,-1,-1};
for(int i=0;i<8;i++){
if(a[x+kx[i]][y+ky[i]].can==0) a[x+kx[i]][y+ky[i]].can = 2;
}
}
void R(int x,int y){
for(int i=y+1;i<=m;i++){
if(s[x][i]!='.') break;
a[x][i].can = 2;
}
for(int i=y-1;i>=1;i--){
if(s[x][i]!='.') break;
a[x][i].can = 2;
}
for(int i=x+1;i<=n;i++){
if(s[i][y]!='.') break;
a[i][y].can = 2;
}
for(int i=x-1;i>=1;i--){
if(s[i][y]!='.') break;
a[i][y].can = 2;
}
}
void P(int x,int y){
if(a[x-1][y-1].can==0&&s[x-1][y-1]=='.') a[x-1][y-1].can = 2;
if(a[x-1][y+1].can==0&&s[x-1][y+1]=='.') a[x-1][y+1].can = 2;
}
void Q(int x,int y){
R(x , y);
B(x , y);
}
void N(int x,int y){
int nx[8] = {-1,-2,-2,-1,1,2,2,1};
int ny[8] = {-2,-1,1,2,2,1,-1,-2};
for(int i=0;i<8;i++){
if(x+nx[i]<1||x+nx[i]>n||y+ny[i]<1||y+ny[i]>m) continue;
if(s[x+nx[i]][y+ny[i]]!='.') continue;
a[x+nx[i]][y+ny[i]].can = 2;
}
}
void add(int x,int y){//链式前向星建边
to[++id] = y;
ne[id] = head[x];
head[x] = id;
}
void init(){//标记r,c数组
int lenr=1;
for(int i=1;i<=n+m-1;i++){
for(int j=1;j<=n+m-1;j++){
if(mp[i][j]==-1) continue;
if(!mp[i][j]) r[i][j] = lenr;
else if(mp[i][j]==1) lenr++;
}
lenr++;
}
maxx = lenr;
int lenc=1;
for(int i=1;i<=n+m-1;i++){
for(int j=1;j<=n+m-1;j++){
if(mp[j][i]==-1) continue;
if(!mp[j][i]) c[j][i] = lenc;
else if(mp[j][i]==1) lenc++;
}
lenc++;
}
for(int i=1;i<=n+m-1;i++){
for(int j=1;j<=n+m-1;j++){
if(!mp[i][j]) add(r[i][j],c[i][j]);
}
}
}
bool dfs(int u){//匈牙利算法
for(int i=head[u];i;i=ne[i]){
int v = to[i];
if(vis[v]) continue;
vis[v] = 1;
if(!d[v] || dfs(d[v])){
d[v] = u;
return true;
}
}
return false;
}
int main(){
scanf("%d %d",&n,&m);
for (int i=1;i<=n;i++){
scanf("%s",s[i]+1);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
node f = {i+j-1 , j+n-i , a[i][j].can};
if(s[i][j]=='.'){
a[i][j] = f;
continue;
}
f.can = 1;
a[i][j] = f;
if(s[i][j]=='B'){
B(i , j);
}
else if(s[i][j]=='K'){
K(i , j);
B(i , j);
}
else if(s[i][j]=='R'){
R(i , j);
B(i , j);
}
else if(s[i][j]=='P'){
P(i , j);
B(i , j);
}
else if(s[i][j]=='Q'){
Q(i , j);
}
else if(s[i][j]=='N'){
N(i , j);
B(i , j);
}
}
}
memset(mp,-1,sizeof(mp));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
mp[a[i][j].xx][a[i][j].yy] = a[i][j].can;
}
}
init();
for(int i=1;i<maxx;i++){
memset(vis,0,sizeof(vis));
if(dfs(i)) ans++;
}
printf("%d\n",ans);
return 0;
}
版权说明:本文为博主原创文章,转载请附上本文链接(https://blog.csdn.net/smart_CZH/article/details/125120137)。
2022-06-18 本文在洛谷用户caizehua的博客中发布。