题解 P3930 【SAC E#1 - 一道大水题 Knight】
洛谷不能自动识别代码了?
这道题我做的时候担心会wa或者t——结果ce了。
最后发现题目数据特别水,加个特判就能ac。0ms。中间有一些判断棋子会不会死的技巧,所以快一些。
然后吐槽下数据,虽然数据水,但也不能造谣啊,为啥n出现了55?幸亏我一开始就开的60的数组。
#include <iostream>
#include <cstdio>
#include <cstring> //一开始没加但xcode编译的过,结果交上去就ce
#include <queue>
using namespace std;
int n;
int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int dy[8] = {2, 1, -1, -2, 2, 1, -1, -2}; //骑士周围八个点
int dx2[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dy2[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; //国王周围八个点
int qx, qy; //起点坐标
int board[60][60]; //判断单点是否死亡,适用于骑士、国王、士兵的攻击范围
int vis[60][60]; //判断是否访问
int r[60]; //判断棋子所在行是否会受到攻击,适用于皇后、城堡的攻击范围
int c[60]; //这个是判断列,和行基本一样
int le[120];
int ri[120]; //判断两条对角线,适用于皇后和主教
//以上判断变量的含义是该行、列、对角线、单点受到威胁的数量,比如两个城堡同时看守着第i行,那么r[i] = 2
char a[60][60]; //存储棋盘
queue <int> q1;
queue <int> q2;
queue <int> q3;
bool able(int x, int y)
{
if (r[x] || c[y] || le[x+y] || ri[x-y+n] || board[x][y]) return 0; //判断棋子所在位置是否安全,即行、列等是否有威胁。
return 1; //没有威胁就是安全
}
void init()
{
memset(a, 0, sizeof(a));
memset(r, 0, sizeof(r));
memset(c, 0, sizeof(c));
memset(le, 0, sizeof(le));
memset(ri, 0, sizeof(ri));
memset(vis, 0, sizeof(vis));
memset(board, 0, sizeof(board));
while (!q1.empty()) { q1.pop(); q2.pop(); q3.pop(); }
} //全清空
void bfs()
{
q1.push(qx); //起点横坐标
q2.push(qy); //起点纵坐标
q3.push(1); //到达这个点再走一步所需的步数,初始为1
while (!q1.empty()) {
int x = q1.front();
int y = q2.front();
if (vis[x][y]) { //不走回头路,但这样的话其实可以有数据卡掉
q1.pop();
q2.pop();
q3.pop();
continue;
}
vis[x][y] = 1; //标为已访问
for (int i = 0; i < 8; i++) { //向周围8个点扩展
int xx = x+dx[i];
int yy = y+dy[i];
if (xx > n || xx < 1 || yy > n || yy < 1) continue; //不能跳出棋盘
if (vis[xx][yy]) continue;
if (a[xx][yy] == 'C')
if (able(xx, yy)) { //吃掉之后仍然不受威胁就吃
r[xx]--;
c[yy]--; //威胁-1
q1.push(xx);
q2.push(yy);
q3.push(q3.front()+1); //入栈,步数+1
continue;
}
if (a[xx][yy] == 'K')
if (able(xx, yy)) {
for (int k = 0; k < 8; k++) board[xx+dx[k]][yy+dy[k]]--;
q1.push(xx);
q2.push(yy);
q3.push(q3.front()+1);
continue;
} //骑士同理
if (a[xx][yy] == 'B')
if (able(xx, yy)) {
le[xx+yy]--;
ri[xx-yy+n]--;
q1.push(xx);
q2.push(yy);
q3.push(q3.front()+1);
continue;
} //主教同理
if (a[xx][yy] == 'Q')
if (able(xx, yy)) {
r[xx]--;
c[yy]--;
le[xx+yy]--;
ri[xx-yy+n]--;
q1.push(xx);
q2.push(yy);
q3.push(q3.front()+1);
continue;
} //皇后同理,只是威胁减少的更多
if (a[xx][yy] == 'X') {
cout << q3.front() << endl;
return;
} //吃掉国王就输出步数并退出
if (a[xx][yy] == 'P')
if (able(xx, yy)) {
board[xx+1][yy-1]--;
board[xx+1][yy+1]--;
q1.push(xx);
q2.push(yy);
q3.push(q3.front()+1);
continue;
} //士兵同理
if (able(xx, yy)) { //如果这个格子以上都不是,那就是空的格子,判断一下就能不能走就好了
q1.push(xx);
q2.push(yy);
q3.push(q3.front()+1);
}
}
}
cout << -1 << endl; //最后没吃掉国王就输出-1
}
int main(void)
{
while (cin >> n) { //多组数据
init(); //初始化
for (int i = 1; i <= n; i++) {
scanf("%s", a[i]+1); //输入技巧,以字符串形式读入
for (int j = 1; j <= n; j++) {
if (a[i][j] == 'O') { qx = i; qy = j; } //起点
if (a[i][j] == 'C' || a[i][j] == 'Q') { r[i]++; c[j]++; } //城堡、皇后所在行列威胁+1
if (a[i][j] == 'K') for (int k = 0; k < 8; k++) board[i+dx[k]][j+dy[k]]++; //骑士周围8个点威胁+1
if (a[i][j] == 'B' || a[i][j] == 'Q') { le[i+j]++; ri[i-j+n]++; } //主教、皇后所在对角线威胁+1
if (a[i][j] == 'X') for (int k = 0; k < 8; k++) board[i+dx2[k]][j+dy2[k]]++; //国王周围8个点威胁+1
if (a[i][j] == 'P') { board[i+1][j-1]++; board[i+1][j+1]++; } //士兵下面两个点威胁+1
}
}
if (!able(qx, qy)) { cout << -1 << endl; continue; } //如果一开始就受到威胁,就要死。我改掉ce之后因为忘加这个特判wa成80分
bfs(); //实际上应该深搜才不会被卡,但会t。所以广搜——数据好一点其实可以卡掉广搜
}
return 0;
}