紫书题解3.象棋 (UVa 1589 综合) 附显示棋盘

原题见https://vjudge.net/problem/UVA-1589

大意是判断黑将军是否被红棋(红帅,红车,红马,红炮)将死。注意可以将帅吃,如果黑将和红帅正对面并且中间没有其他棋子,算黑棋胜利。

代码没有AC,但是udebug里面的1000多个数据全部通过。找不到问题,也不知道怎么回事,我就当格式出了错误吧。

(码了几天,估计20个小时了,还是不行。。。)

思路:

为了方便判断,定义一个10*9的数组用于存储棋子。

1、判断黑将是否能吃掉红帅:

如果黑将能直接吃掉红帅,那么黑棋就赢了,后面也不用判断了,所以一开始先判断这个。

判断起来也很简单,从黑将的位置开始纵向搜索就行了。

 

2、判断黑将是否有路可走(是否被将死)

黑将的移动范围有限,只能在如下的红色区域内移动

所以首先要先求出黑将能移动的点,然后判断这些点能不能移动。

当然,棋子A可能落在黑将能走的范围上。如果那个点没有被除了A以外的其他棋子攻击,

那我们就把这个棋子拿走,当做被黑帅吃了。

 

我们对黑将能走的所有点都进行,如果每个点都受到攻击,毋庸置疑黑将被将死了。

为了方便起见,下面把黑将能走的点称为“移动点

 

(1)移动点来自马的攻击:

判断移动点是否受到马的攻击最为简单,即使要考虑到“别马脚”这种情况。

如下图所示,马最多能在8个点(红色)攻击移动点(蓝色),并且这8个点又被4个点(绿色)别住马脚了:

若移动点坐标为(0,0),那么这8个红色点的坐标分别为:

{-1,-2},{-2,-1},{1,-2},{2,-1},{-1,2},{-2,1},{1,2},{2,1}

对应地,有4个点能别住这些马,使其无法攻击到移动点。为{-1,-1},{1,-1},{-1,1},{1,1}

为了与上面8个红色的点一一对应,记为:

{-1,-1},{-1,-1},{1,-1},{1,-1},{-1,1},{-1,1},{1,1},{1,1}

上面这些坐标我叫做偏移量,

到时候通过移动点坐标+偏移量的方式就能将8个红点+4个绿点表示出来。

再判断一下是否超出棋盘范围,就能判断该点是否被马攻击了。

 

(2)来自车,帅的攻击

判断来自车,帅的攻击较为麻烦,因为要从移动点开始向四周探索。要写四个循环,循环内容还差不多,感觉十分繁琐,所以觉得较为麻烦。

实际上思路也是很简单的:向四周探索,看看第一个被发现的红棋是不是车或帅就行了。

 

(3)来自炮的攻击

最麻烦就是这个了,从移动点开始向四周探索,要判断第二个遇到的红棋是不是炮,所以比(2)还要复杂。

当然可以化简一下:

①如果第一个探索到的红棋是车或帅,就不必继续探索看看有没有炮了,反正这个点被车或帅攻击,迟早死翘翘

②如果第一个探索到的红棋是炮或马,就继续探索看看有没有炮

 

以上就是本题的大致思路了,下面是代码:

为了方便观察,附增上了棋盘显示功能,

 

B表示黑将,*表示安全的移动点

G表示红将,R表示红车,H表示红马,C表示红炮

如果黑将能直接吃掉红帅,什么安全的移动点都不显示出来,直接输出答案

 

 

代码如下:

/**
 UVa 1589


**/

#include<stdio.h>
#include<iostream>
#include<windows.h>
using namespace std;

char bou[10][10];

//---------------------------------------------调试代码----------------------------------------------------------------
void color(int b){
    HANDLE hConsole = GetStdHandle((STD_OUTPUT_HANDLE)) ;
    SetConsoleTextAttribute(hConsole, b) ;//颜色控制
}//颜色控制模块 

//界面显示(测试用)
//B:黑方将军 *:黑方将军可移动范围
//G:红方将军 R:红方车 H:红方马 C:红方炮 #:红方攻击范围
void showBou(){
	printf("\n\n");
	for(int i=0;i<10;++i){
		printf("     ");
		for(int j=0;j<9;++j){
			if(bou[i][j]!=NULL){
				bou[i][j]=='B' || bou[i][j]=='*' ? color(11):color(12);
				printf("%c",bou[i][j]);color(7);
			}else{printf("+");}
			if(j<8)printf("---");
		}
		if(i<9){
			printf("\n     ");
			if(i!=4){
				for(int k=0;k<9;++k){
					if(((i==0 || i==7) && k==3) || ((i==1 || i==8) && k==4)){printf("| \\ ");}
					else if(((i==0 || i==7) && k==4) || ((i==1 || i==8) && k==3)){printf("| / ");}
					else printf("|   ");
				}
			}else{printf("      -----汉界-----楚河-----");}
		}
		printf("\n");
	}
}

//----------------------------------------------------------------------------------------------------------------------------*/

//--------------------------------核心代码------------------------------------------------------------------------------------

void clear(){
	for(int i=0;i<10;++i){
		for(int j=0;j<10;++j){
			bou[i][j]=NULL;
		}
	}
}

//来自马的攻击
int HAT[8][2]={{-1,-2},{-2,-1},{1,-2},{2,-1},{-1,2},{-2,1},{1,2},{2,1}};//如果某个格子坐标为(0,0),可能被以下8个点的马攻击到(前提是马没有被别马脚)
int HHL[8][2]={{-1,-1},{-1,-1},{1,-1},{1,-1},{-1,1},{-1,1},{1,1},{1,1}};//以上8个点对应被别马脚的地方
bool horse(int y,int x){//黑将军某个可移动点的坐标
	int m,n;//马的坐标
	int m1,n1;//别马脚的地方
	for(int i=0;i<8;++i){
		m=x+HAT[i][0],n=y+HAT[i][1];
		if(m>=0 && m<10 && n>=0 && n<9){
			m1=HHL[i][0]+x,n1=HHL[i][1]+y;
			if(bou[m][n]=='H' && (bou[m1][n1]==NULL || bou[m1][n1]=='*' || bou[m1][n1]=='#')){//该点有马而且没被别住
				return true;
			}
		}
	}
	return false;
}


//来自将军和车的攻击
bool chariot(int x,int y){

	bool t=false;

	//横
	for(int i=x-1;i>=0;--i){
		if(bou[y][i]=='R') t=true;
		if(bou[y][i]=='C' || bou[y][i]=='H') break;
	}
	for(int i2=x+1;i2<10;++i2){
		if(bou[y][i2]=='R') t=true;
		if(bou[y][i2]=='C' || bou[y][i2]=='H') break;
	}

	//纵
	for(int j=y-1;j>=0;--j){
		if(bou[j][x]=='R' || bou[j][x]=='G') t=true;
		if(bou[j][x]=='C' || bou[j][x]=='H') break;
	}
	for(int j2=y+1;j2<10;++j2){
		if(bou[j2][x]=='R' || bou[j2][x]=='G') t=true;
		if(bou[j2][x]=='C' || bou[j2][x]=='H') break;
	}
	return t;
}


//来自炮的攻击
bool cannon(int x,int y){//黑将军某个可移动点的坐标

	//寻找炮架。如果找到车或帅的话,我们不用管,反正也难逃一死了

	int flag=false;//炮架

	//横
	for(int i=x-1;i>=0;--i){
		if(bou[y][i]=='C' && flag) return true;
		if(bou[y][i]!=NULL && bou[y][i]!='C' && flag) return false;
		if(bou[y][i]=='C' || bou[y][i]=='H') {
			flag=true;
		}
	}flag=false;

	for(int i2=x+1;i2<10;++i2){
		if(bou[y][i2]=='C' && flag) return true;
		if(bou[y][i2]!=NULL && bou[y][i2]!='C' && flag) return false;
		if(bou[y][i2]=='C' || bou[y][i2]=='H') {
			flag=true;
		}
	}flag=false;

	//纵
	for(int j=y-1;j>=0;--j){
		if(bou[j][x]=='C' && flag) return true;
		if(bou[j][x]!=NULL && bou[j][x]!='C' && flag) return false;
		if(bou[j][x]=='C' || bou[j][x]=='H') {
			flag=true;
		}
	}flag=false;

	for(int j2=y+1;j2<10;++j2){
		if(bou[j2][x]=='C' && flag) return true;
		if(bou[j2][x]!=NULL && bou[j2][x]!='C' && flag) return false;
		if(bou[j2][x]=='C' || bou[j2][x]=='H') {
			flag=true;
		}
	}

	return false;

}


//判断是否被将死
int judge(int m,int n){


	//看看黑将是否能吃掉红帅
	for(int z=m;z<10;++z){
		if(bou[z][n]=='C' || bou[z][n]=='H' || bou[z][n]=='R') break;
		if(bou[z][n]=='G') return 1;
	}

	//确定黑方可移动范围
	int canMove=0;
	int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
	int x,y;	

	//把黑将能吃的棋子都消灭掉
	for(int i1=0;i1<4;++i1){
		x=n+dir[i1][0],y=m+dir[i1][1];
		if(x>2 && x<6 && y>=0 && y<3){

		//	bou[y][x]='#';
		}
	}
	for(int i=0;i<4;++i){
		x=n+dir[i][0],y=m+dir[i][1];
		if(x>2 && x<6 && y>=0 && y<3){
			if(horse(x,y) || chariot(x,y) || cannon(x,y)){
			//	bou[y][x]='#';
			}else{
				bou[y][x]='*';
				canMove++;
			}
		}
	}
	return canMove;
}

int out[10000];
int main(){
	int t,m,n,c;
	int f=0;
	while(scanf("%d",&t)>0){
		if(t>0){
			clear();
			int w,q;
			scanf("%d %d",&w,&q);
			bou[w-1][q-1]='B';
			while(--t>=0){
				getchar();
				c=getchar();
				scanf("%d%d",&m,&n);
				bou[m-1][n-1]=c;
			}
			judge(w-1,q-1)==0 ? printf("YES\n") : printf("NO\n");
		//	judge(w-1,q-1)==0 ? out[f]=1:out[f]=0;//一次性输出所有答案时用
			++f;
			showBou();//显示棋盘
		}else{
			
			break;
		}
	}
//	printf("\n");
	/*一次性输出所有答案
	for(int i=0;i<f;++i){
		printf("%s\n",out[i]==1 ? "YES" : "NO");
	}
	printf("\n");
	*/
	return 0;
}

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值