【洛谷P5380】鸭棋

题目

题目链接:https://www.luogu.com.cn/problem/P5380

题目背景

鸭棋是一种风靡鸭子界的棋类游戏。事实上,它与中国象棋有一些相似之处,但规则不尽相同。在这里,我们将为你介绍鸭棋的规则。

同时,我们下发了一个模拟鸭棋规则的玩具,你可以结合这个玩具理解题目(也可以在 AK 后与你的队友进行对弈)。详情请见「玩具使用说明」。

鸭棋在一个 \(10\times 9\)\(10\)\(9\) 列)的网格棋盘上进行,网格上的每个格点都可以有棋子停留。对弈双方一方执红(red)棋、另一方执蓝(blue)棋,双方轮流执行操作,轮到一位玩家操作时,他必须选择一枚自己的棋子,并按照规则进行一步移动。

鸭棋发明者鸭子德规定一局鸭棋由红方执先手,并设计了初始棋盘布局如下:

initial_board.png

棋子类型与走子规则

棋子分为 \(7\) 类,下面介绍了它们的名字以及它们的移动规则。介绍移动规则时,我们默认棋子所处位置为 \(\left( x,y\right)\)(表示第 \(x\) 行的第 \(y\) 列,下同),并列出它可以到达的位置:

  • captain):可达的位置共 \(4\) 个,包括 \(\left(x\pm 1,y\right)\)\(\left(x,y\pm 1\right)\)
  • guard):可达的位置共 \(4\) 个,包括 \(\left(x\pm 1,y\pm 1\right)\)\(\left(x\pm 1,y\mp 1\right)\)
  • elephant):可达的位置至多 \(4\) 个,对于任意 \(s_x,s_y\in \left\{ 1,-1\right\}\),分别有:
    • 如果位置 \(\left(x+s_x\times 1 ,y+ s_y\times 1\right)\)无任意一方的棋子停留,则 \(\left( x+s_x \times 2,y+s_y \times 2\right)\) 为一个可达的位置。
  • horse):可达的位置至多 \(8\) 个,对于任意 \(s_x,s_y\in \left\{ 1,-1\right\}\),分别有:
    • 如果位置 \(\left(x+s_x\times 1 ,y\right)\)无任意一方的棋子停留,则 \(\left( x+s_x \times 2,y+s_y \times 1\right)\) 为一个可达的位置。
    • 如果位置 \(\left(x ,y+ s_y \times 1 \right)\)无任意一方的棋子停留,则 \(\left( x+s_x \times 1,y+s_y \times 2\right)\) 为一个可达的位置。
  • car):可在不跨越其他棋子的前提下,到达同行或同列的所有其他位置。
  • duck):可达的位置至多 \(8\) 个,对于任意 \(s_x,s_y\in \left\{ 1,-1\right\}\),分别有:
    • 如果位置 \(\left(x+s_x\times 2 ,y+s_y \times 1\right),\left(x+s_x\times 1 ,y\right)\) 上均无任意一方的棋子停留,则 \(\left( x+s_x \times 3,y+s_y \times 2\right)\) 为一个可达的位置。
    • 如果位置 \(\left(x+s_x \times 1 ,y+ s_y \times 2 \right),\left(x ,y+ s_y \times 1 \right)\) 上均无任意一方的棋子停留,则 \(\left( x+s_x \times 2,y+s_y \times 3\right)\) 为一个可达的位置。
  • soldier):可达的位置共 \(8\) 个,包括 \(\left(x\pm 1,y\right)\)\(\left(x,y\pm 1\right)\)\(\left(x\pm 1,y\pm 1\right)\)\(\left(x\pm 1,y\mp 1\right)\)

除上面描述的规则之外,棋子移动还有如下额外规则:

  • 不能将棋子移动到棋盘外的某个位置。
  • 玩家不能将棋子移动到已经停留了己方棋子的位置。
  • 如果玩家将棋子移动到了一个已经停留了对方棋子的位置,那么原本停留在该位置上的这个对方棋子将被移出游戏。

胜利条件与将军局面

玩家在这个游戏中的目标是将对方的移出游戏。一旦一方的被移出游戏,则另一方立即宣告胜利。

对于一个棋盘的状态,如果存在一方有一步合法的操作能够将另一方的移出游戏,则我们说当前局面是一个将军的局面。需要友情提示的是,根据定义,将军局面的形成包括(但不限于)如下这些可能:

  1. 一方将一枚棋子移动到可以攻击对方的位置

  2. 在己方受到威胁时不采取措施躲避

  3. 主动将移动至会受到攻击的位置

除此之外,需要特别说明的是,游戏结束后,由于双方不可再操作,因此不可能出现将军局面,即便此时另一方王处于被「攻击」的位置。

题目描述

今年的 IDCC(International Duck Chess Competition,国际鸭棋大赛)正在如火如荼地进行着。你观摩了一场精彩绝伦的比赛,但你对对弈过程的记忆已经模糊不清了,只有系统留下的他们的操作序列,序列中的每个操作为当前操作者试图移动某个位置的棋子至另一个位置。你希望用这个序列,来复现出整局棋局的对弈过程。即,对于每步操作,你需要首先判其是否合法,若合法,则进一步求出

  1. 这步操作移动了哪个棋子。
  2. 这步操作后,是否存在棋子被移出游戏,如有则还需求出被移出游戏的棋子。
  3. 这步操作后,是否形成将军局面。
  4. 这步操作后,游戏是否结束。

可能包含的不合法情况如下:

  • 此步移动的初始位置无己方棋子停留。
  • 此步移动的初始位置有己方棋子停留,但移动不符合规则。
  • 游戏已经结束。

序列中的不合法操作是需要被忽略的。比如,如果轮到红方移动,此时序列中的当前操作恰好是不合法的,则这个操作将被忽略,序列中的下一步操作将成为红方这步的操作(如仍不合法则继续忽略,直至出现合法的操作)。

输入格式

第一行一个非负整数 \(Q\),表示操作序列的长度。接下来依次描述每个操作。

接下来 \(Q\) 行,每行 \(4\) 个整数 \(x_s, y_s, x_t, y_t\)\(0\leq x_s,x_t < 10\)\(0\leq y_s,y_t < 9\)),描述一个欲将 \(\left(x_s,y_s\right)\) 处棋子移动到 \(\left(x_t,y_t\right)\) 的操作。在这里,我们规定左下角(即红方摆放的位置,图见「题目背景」)为 \(\left(0,0\right)\)

保证 \(Q\leq 1000\)

输出格式

输出 \(Q\) 行,对于每个操作依次输出复现结果。每行输出一个操作的结果:

  • 如果该操作为不合法操作,则请输出 Invalid command
  • 如果为合法操作,则依次回答「题目描述」中的问题 \(1\sim 4\)
    • 被移动的棋子用 [颜色] [类型](注意中间包含空格)来描述,请使用它们的英文名称(见「题目背景」)。如,红象为 red elephant,蓝王为 blue captain
    • 被移出游戏的棋子的描述方式与上面类似。特别地,如果无棋子被移出游戏,则该问题的答案为 NA
    • yesno 分别表示形成、不形成将军局面。
    • yesno 分别表示游戏结束、游戏不结束。
    • ;(分号)将所有问题的答案隔开。
    • 比如,四个问题的答案分别为:被移动的棋子是蓝车,无棋子被移出游戏,形成将军局面,游戏未结束。则该行应输出 blue car;NA;yes;no

思路

复制粘贴题目真 nm 麻烦
本来想敲一道比较恶心的模拟练练手的,但是敲完后发现这道题题目是很长,但是其实不难。
主要分成 4 个部分来做:

  1. 判断该点是否有棋子
  2. 判断该棋子能否走到目标位置(行走方式、是否有障碍)
  3. 判断目标位置是否有敌方棋子
  4. 判断目标位置是否造成将军

首先设 \(col[x][y]\) 表示 \((x,y)\) 的棋子的颜色,\(type[x][y]\) 表示 \((x,y)\) 的棋子的种类。\((sx,sy)(tx,ty)\) 分别是起点和终点。
判断是否有棋子很简单,直接判断 \(col[sx][sy]\) 是否是现在移动的那一方即可。
如果是,那么就暴力判断能否到达目标点。注意象、马、车、鸭移动是需要判断障碍的。
如果可以移动,那么就输出第一问和第二问。否则该步骤无效。
接下来判断是否将军或者结束。我们找到双方的王,然后依次枚举每一个位置 \((i,j)\),判断 \(col[i][j]\) 是否与现在枚举到的王不一样。如果是,那么判断 \((i,j)\) 能否走到王的位置即可。如果可以,输出 yes;no;否则输出 no;no
注意在找王的时候如果没找到,那么说明已经结束了。直接退出循环,输出 no;yes,并且剩下的询问全部输出 Invalid command 即可。
思维难度和代码难度都不大。

代码

老年人手速都 \(80min\) 内搞定这题,忘记关掉文件输入不算的话一次就过了

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fail { printf("Invalid command\n"); continue; }
using namespace std;

const int N=15;
int Q,now,col[N][N],type[N][N];

void prework()
{
	now=1;
	for (int i=1;i<=9;i++)
		col[1][i]=1,col[10][i]=2;
	for (int i=1;i<=9;i+=2)
		col[4][i]=1,col[7][i]=2;
	col[3][1]=col[3][9]=1; col[8][1]=col[8][9]=2;
	
	type[1][5]=type[10][5]=1;
	type[1][4]=type[1][6]=type[10][4]=type[10][6]=2;
	type[1][3]=type[1][7]=type[10][3]=type[10][7]=3;
	type[1][2]=type[1][8]=type[10][2]=type[10][8]=4;
	type[1][1]=type[1][9]=type[10][1]=type[10][9]=5;
	type[3][1]=type[3][9]=type[8][1]=type[8][9]=6;
	for (int i=1;i<=9;i+=2)
		type[4][i]=type[7][i]=7;
}

//    1  2  3  4  5  6  7
//   王 士 象 马 车 鸭 兵 

bool move(int sx,int sy,int tx,int ty)
{
	if (col[tx][ty]==col[sx][sy]) return 0;
	if (type[sx][sy]==1)
	{
		if (tx==sx && (ty==sy-1 || ty==sy+1)) return 1;
		if (ty==sy && (tx==sx-1 || tx==sx+1)) return 1;
		return 0;
	}
	if (type[sx][sy]==2)
	{
		if (abs(sx-tx)==1 && abs(sy-ty)==1) return 1;
		return 0;
	}
	if (type[sx][sy]==3)
	{
		if (abs(sx-tx)==2 && abs(sy-ty)==2 && !col[(sx+tx)/2][(sy+ty)/2]) return 1;
		return 0;
	}
	if (type[sx][sy]==4)
	{
		if (tx==sx-2 && ty==sy-1 && !col[sx-1][sy]) return 1;
		if (tx==sx-1 && ty==sy-2 && !col[sx][sy-1]) return 1;
		if (tx==sx+1 && ty==sy-2 && !col[sx][sy-1]) return 1;
		if (tx==sx+2 && ty==sy-1 && !col[sx+1][sy]) return 1;
		if (tx==sx+2 && ty==sy+1 && !col[sx+1][sy]) return 1;
		if (tx==sx+1 && ty==sy+2 && !col[sx][sy+1]) return 1;
		if (tx==sx-1 && ty==sy+2 && !col[sx][sy+1]) return 1;
		if (tx==sx-2 && ty==sy+1 && !col[sx-1][sy]) return 1;
		return 0;
	}
	if (type[sx][sy]==5)
	{
		if (sx==tx)
		{
			for (int i=min(sy,ty)+1;i<max(sy,ty);i++)
				if (col[sx][i]) return 0;
			return 1;
		}
		if (sy==ty)
		{
			for (int i=min(sx,tx)+1;i<max(sx,tx);i++)
				if (col[i][sy]) return 0;
			return 1;
		}
		return 0;
	}
	if (type[sx][sy]==6)
	{
		if (tx==sx-3 && ty==sy-2 && !col[sx-1][sy] && !col[sx-2][sy-1]) return 1;
		if (tx==sx-2 && ty==sy-3 && !col[sx][sy-1] && !col[sx-1][sy-2]) return 1;
		if (tx==sx+2 && ty==sy-3 && !col[sx][sy-1] && !col[sx+1][sy-2]) return 1;
		if (tx==sx+3 && ty==sy-2 && !col[sx+1][sy] && !col[sx+2][sy-1]) return 1;
		if (tx==sx+3 && ty==sy+2 && !col[sx+1][sy] && !col[sx+2][sy+1]) return 1;
		if (tx==sx+2 && ty==sy+3 && !col[sx][sy+1] && !col[sx+1][sy+2]) return 1;
		if (tx==sx-2 && ty==sy+3 && !col[sx][sy+1] && !col[sx-1][sy+2]) return 1;
		if (tx==sx-3 && ty==sy+2 && !col[sx-1][sy] && !col[sx-2][sy+1]) return 1;
		return 0;
	}
	if (type[sx][sy]==7)
	{
		if (abs(sx-tx)<=1 && abs(sy-ty)<=1) return 1;
		return 0;
	}
	return 0;
}

void print_type(int sx,int sy)
{
	if (col[sx][sy]==1) printf("red ");
	if (col[sx][sy]==2) printf("blue ");
	
	if (type[sx][sy]==1) printf("captain;");
	if (type[sx][sy]==2) printf("guard;");
	if (type[sx][sy]==3) printf("elephant;");
	if (type[sx][sy]==4) printf("horse;");
	if (type[sx][sy]==5) printf("car;");
	if (type[sx][sy]==6) printf("duck;");
	if (type[sx][sy]==7) printf("soldier;");
}

void find_captain(int k,int &x,int &y)
{
	x=y=-1;
	for (int i=1;i<=10;i++)
		for (int j=1;j<=9;j++)
			if (col[i][j]==k && type[i][j]==1)
			{
				x=i; y=j;
				return;
			}
}

int main()
{
	freopen("data.txt","r",stdin);
	prework();
	scanf("%d",&Q);
	int sx,sy,tx,ty;
	while (Q--)
	{
		scanf("%d%d%d%d",&sx,&sy,&tx,&ty);
		sx++; sy++; tx++; ty++;
		
		if (!col[sx][sy] || col[sx][sy]!= now) fail;
		if (!move(sx,sy,tx,ty)) fail;
		
		print_type(sx,sy);
		if (col[tx][ty]) print_type(tx,ty);
			else printf("NA;");
		
		col[tx][ty]=col[sx][sy]; type[tx][ty]=type[sx][sy];
		col[sx][sy]=type[sx][sy]=0;
		
		bool flag=0;
		for (int k=1,x,y;k<=2;k++)
		{
			find_captain(k,x,y);
			if (x<0)
			{
				printf("no;yes\n");
				goto finish;
			}
			for (int i=1;i<=10;i++)
				for (int j=1;j<=9;j++)
					if (col[i][j]==3-k && move(i,j,x,y)) flag=1;
		}
		if (flag) printf("yes;no\n");
			else printf("no;no\n");
		now=3-now;
	}
	finish:
	for (int i=1;i<=Q;i++) fail;
	return 0;
}
posted @ 2020-06-28 19:16  stoorz  阅读(441)  评论(0编辑  收藏  举报