UVA1589 象棋 + UVA 220 黑白棋

UVA1589 象棋

象棋是中国最受欢迎的两人桌游之一。该游戏代表了两支军队之间的战斗,目的是捕获敌人的“将军”。

现在我们介绍一些象棋的基本规则。

象棋在10×9的棋盘上演奏,并将棋子放置在交叉点(点)上。左上角是(1,1),右下角是(10,9)。有两组用黑色或红色汉字标记的棋子,分别属于两个玩家。在游戏中,每个玩家依次将一块从其占据的位置移动到另一点。没有两个片段可以同时占据同一点。可以将一个棋子移动到一个敌方棋子占据的点上,在这种情况下,该敌棋子被“捕获”并从棋盘上移走

当某一方的将军有被敌方玩家下一步行动俘虏的危险时,就称敌方玩家已经 “交了支票” (处于优势地位)。如果该将军玩家没有采取任何行动来阻止敌方玩家下一步的占领,则这种情况称为 “将死”

我们仅使用以下四种介绍:
在这里插入图片描述将军:将军可以垂直或水平移动并捕获一个点,除非“飞行将军”的情况(参见上图),否则不能离开“宫殿”。 “飞行将军”意味着:如果它和对方的将军位于同一直线上,且这条连线上没有其他棋子,则它就可以把对方的将军捕获。
在这里插入图片描述车:可以水平或垂直方向移动任意个无阻碍的点,即,想要移动到的位置必须和所处位置之间没有隔着其他棋子。
在这里插入图片描述炮:炮可以像车一样在水平和垂直方向上移动,但它必须跳过一个棋子来吃掉对方的一个棋子。

在这里插入图片描述马:马有8种跳跃动作来移动和捕获。但是,如果有任何棋子在水平或垂直方向上都偏离马的点,则无法沿该方向移动或捕获(请参见下图),这被称为“憋马腿”。
在这里插入图片描述

现在的情况是:只有一个黑人将军,以及一个红色将军和几个红色战车、大炮和马匹,且红方已发出支票(占据优势)。现在轮到黑方了。您的工作是确定这种情况是否会输棋。

输入

输入输入包含不超过40个测试用例。对于每个测试用例,第一行包含三个整数,分别代表红色碎片N的数量(2≤N≤7)和黑色将军的位置。接下来的N行包含N个红色片段的详细信息。每行都有一个char和两个整数,分别代表该棋子的类型和位置(char字母“ G”代表将军,“ R”代表战车,“ H”代表马,“ C”代表大炮)。我们保证情况是合法的,并且红方已交付了支票。在两个测试用例之间有一个空白行。输入以“ 0 0 0”结尾。

输出

对于每个测试用例,如果情况已死,则输出一个单词“YES”,否则输出单词“NO”。


题目大意概述

其实上面可以不用太理解,直接看这里吧:

红方目前处于优势地位,下一步是黑方出旗,我们的目的是判断这关键的一步——黑方的出旗是导致自己输掉,还是不会输掉。如果发生输棋,则输出“YES”,否则,输出“NO”。其实有两种情况会导致“输棋”:自己的将被对方的将“将死”,或,自己的将被对方的某个棋子吃掉。继续往下看,就彻底理解这两种情况了。

思路

第一步就是要进行黑将的移动,判断出可行的移动方式;然后,再进一步判断做出的这一可行移动是否会发生“将死”情况。

1

找可行的移动方式是很容易的,只需要判断移动到的位置的是否在“九宫”内:

如果向上走,则判断 b_row - 1 > 0 否?
如果向下走,则判断 b_row + 1 < 4 否?
如果向左走,则判断 b_column - 1 > 3 否?
如果向右走,则判断 b_column + 1 < 7 否?

2

找到一个可行的移动方式后,就需要判断黑将移动到这里后的局面是否“将死”。这里就需要我们非常理解题目上介绍的那四种规则了。就是利用上面的四种规则进行判断是否“将死”。下面再一一按照写代码的思路做出阐述。

  • 黑将移动到某个位置后,这个位置和红将所在位置位于同一列,且中间没有隔有其他棋子,则“将死”。

  • 车能够在棋盘上的任何位置上进行移动,所以要分别进行行、列的判断:

  1. 黑将移动到某个位置后,这个位置和红车在同一列,且中间没有隔有其他棋子,则红车可以吃掉黑将。
  2. 黑将移动到某个位置后,这个位置和红车在同一行,且中间没有隔有其他棋子,则红车可以吃掉黑将。
  • 炮能够在棋盘上的任何位置上进行移动,所以要分别进行行、列的判断
  1. 黑将移动到某个位置后,这个位置和红炮在同列,且中间只隔了一个棋子,红炮可以将黑将吃掉。
  2. 黑将移动到某个位置后,这个位置和红炮在同行,且中间只隔了一个棋子,红炮可以将黑将吃掉。
  • 判断黑将移动到的这个位置是否是“蹩马腿”的局面指的是:图中对号或叉号旁的点都是马不可以到达的地方,但前提是马的上/下/左/右没有棋子。这样的话,下一步红方就能让马吃掉黑棋,最终导致黑棋输。(马的移动方向——水平或垂直移动一点,然后再沿对角线向左或者向右移动一点。)

代码需要做的是:先判断红马的上下左右点上是否有障碍物,如果其中某一方位没有障碍物,则判断黑将新到达的位置是否是沿左对角线或右对角线的位置,如果是的话,则黑棋输,因为下一步红方就能让马吃掉黑棋。


(似乎不管是“将死”还是“被吃掉”,都可以直接成为“将死”…下面也不再进行区分)

AC代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

//黑将的四个移动方向(X2[i], Y2[i])
int X2[] = {1,0,0,-1}; //X表示行,所以-1表示向上 
int Y2[] = {0,1,-1,0}; //Y表示列,所以-1表示向左 
vector<char> cate(10); //红方棋子的类别 
vector<pair<int,int>> pos(10); //红方棋子的位置 


//黑将移动到的位置与所在索引为i的位置的红棋位于同一行或同一列的情况下,计算间隔的棋子数。
vector<pair<int,int>> mid(int x1, int y1, int x2, int y2)
{
    vector<pair<int,int>> result;
    if(x1==x2) //如果这两个棋子位于同一行
    {
        if(y1>y2)
        {
            for(int k = 0;k < cate.size();k ++)
            {
                if(pos[k].first==x1&&pos[k].second<y1&&pos[k].second>y2)
                    result.push_back({pos[k].first,pos[k].second});
            }
        }
        else if(y1<y2)
        {
            for(int k = 0;k < cate.size();k ++)
            {
                if(pos[k].first==x1&&pos[k].second>y1&&pos[k].second<y2)
                    result.push_back({pos[k].first,pos[k].second});
            }
        }
    }
    else if(y1==y2) //如果这两个棋子位于同一列 
    {
        if(x1>x2)
        {
            for(int k = 0;k < cate.size();k ++)
            {
                if(pos[k].second==y1&&pos[k].first>x2&&pos[k].first<x1)
                    result.push_back({pos[k].first,pos[k].second});
            }
        }
        else if(x1<x2)
        {
            for(int k = 0;k < cate.size();k ++)
            {
                if(pos[k].second==y1&&pos[k].first<x2&&pos[k].first>x1)
                    result.push_back({pos[k].first,pos[k].second});
            }
        }
    }
    return result;
} 

bool inChessBoard(int x,int y) //是否在棋盘上
{
    return x>0 && y>0 && x<=10 && y<=9;
}

bool inPalace(int x,int y) //是否在九宫内
{
    return y>=4 && y<=6 && x<=3;
}
bool blackGSurvive(int BGx, int BGy)
{
    int i;

    for(int i = 0; i < 4; i++) //黑将的四个移动方向 
    {
        int newX = BGx + X2[i];
        int newY = BGy + Y2[i];

        if(inChessBoard(newX,newY) && inPalace(newX,newY))
        {
            int j;
            for(j = 0; j < cate.size(); j++) //遍历红方棋子 
            {
                if(cate[j]=='G')
                {
                    if(pos[j].second == newY) //位于同一列 
                    {
                        vector<pair<int,int>> tm = mid(pos[j].first,pos[j].second,newX,newY);
                        if(tm.size()==0) //输 
                            break;
                    }
                }
                else if(cate[j]=='R')
                {
                    if(pos[j].first==newX) //位于同一行 
                    {
                        if(pos[j].second==newY)
                        {
                            continue;
                        }
                        vector<pair<int,int>> tm = mid(pos[j].first,pos[j].second,newX,newY);
                        if(tm.size()==0) //输 
                            break;
                    }
                    else if(pos[j].second==newY) //位于同一列 
                    {
                        vector<pair<int,int>> tm = mid(pos[j].first,pos[j].second,newX,newY);
                        if(tm.size()==0) //输 
                            break;
                    }
                }
                else if(cate[j]=='C')
                {
                    if(pos[j].first==newX) //位于同一行 
                    {
                        vector<pair<int,int>> tm = mid(pos[j].first,pos[j].second,newX,newY);
                        if(tm.size()==1&&tm[0].second!=newY
                        || tm.size()==2&&(tm[0].second==newY||tm[1].second==newY))  //输 
                            break;
                    }
                    else if(pos[j].second==newY) //位于同一列 
                    {
                        vector<pair<int,int>> tm = mid(pos[j].first,pos[j].second,newX,newY);
                        if(tm.size()==1&&tm[0].first!=newX
                        || tm.size()==2&&(tm[0].first==newX||tm[1].first==newX))  //输 
                            break;
                    }
                }
                else if(cate[j]=='H')
                {
                    int shang = 1,zuo = 1,you = 1,xia = 1;//判断马的上下左右是否已有棋子,初始化为1表示没有棋子 
                    for(int k = 0; k < cate.size(); k ++)
                    {
                        if(j!=k && pos[k].second==pos[j].second && pos[k].first==pos[j].first-1)
                            shang = 0;
                        if(j!=k && pos[k].second==pos[j].second && pos[k].first==pos[j].first+1)
                            xia = 0;
                        if(j!=k && pos[k].second==pos[j].second-1 && pos[k].first==pos[j].first)
                            zuo = 0;
                        if(j!=k && pos[k].second==pos[j].second+1 && pos[k].first==pos[j].first)
                            you = 0;
                    }
                    //在上/下/左/右没有棋子的情况下,继续判断黑将是否移动到了马可以跳跃到的地方 
                    if(shang)
                    {
                    	/*
							这里if语句里是黑将不可行的位置,
							所以判断黑将新的位置是否真的是在这个不可行的位置上,即判断是否相等,
							如果相等的话,则表明黑将移到了不可行的位置上,所以黑将输。
							(这个位置之所以是输的位置,是因为黑将移动到这里后,
							下一步红方就可以让自己的马吃掉这个黑将,
							所以黑将最终的结果是输。) 
							下同。 
						*/
                        if(pos[j].first-2==newX && pos[j].second-1==newY
                                || pos[j].first-2==newX && pos[j].second+1==newY)  
                            break;
                    }
                    if(xia)
                    {
                        if(pos[j].first+2==newX&&pos[j].second-1==newY
                                || pos[j].first+2==newX&&pos[j].second+1==newY)
                            break;
                    }
                    if(zuo)
                    {
                        if(pos[j].first-1==newX&&pos[j].second-2==newY
                                || pos[j].first+1==newX&&pos[j].second-2==newY)
                            break;
                    }
                    if(you)
                    {
                        if(pos[j].first-1==newX&&pos[j].second+2==newY
                                || pos[j].first+1==newX&&pos[j].second+2==newY)
                            break;
                    }
                }
            }
            /*
            	上面跳出循环的原因都是因为黑将输了,
				所以一旦最终j == cate.size(),则说明没有跳出过循环,
				说明黑将一直没有输.
			*/ 
            if(j == cate.size()) 
            {
                return true;
            }
        }
    }
    return false;
}

int main()
{
    int N, BGx, BGy;
    while(cin >> N >> BGx >> BGy)
    {
        if(N == BGx && N == BGy && N == 0)
            break;

        while(N--)
        {
            char type; //象棋的类别(将/马/车/炮) 
            int Rx, Ry; 
            cin >> type >> Rx >> Ry;
            cate.push_back(type);
            pos.push_back({Rx,Ry});
        }
        
        if(blackGSurvive(BGx, BGy))
            printf("NO\n");
        else
            printf("YES\n");
            
        cate.clear();
        pos.clear();
    }
    return 0;
}

UVA 220 黑白棋

一共有八个方向去遍历,你还想真的写八份代码啊???当然不是!又要进行函数的抽取了!

  • 把【找黑/白玩家的可行棋子】抽成一个函数
  • 把【判断是否有可行棋子】、【打印可行棋子】抽成一个函数(因为都需要遍历可行棋子数组)
  • 把黑白棋表示为 1 和 0

AC代码

#include <stdio.h>
#include <iostream>
#include <vector>
#include <math.h>
#include <algorithm>
#include <queue>
#include <string.h>
#include <set>
#include <stack>
#include <stdlib.h>
#include <time.h>
using namespace std;

char mp[10][10]; //储存棋局
int f[2][10][10];//标记黑白子可执行合法操作的点(0为白棋,1为黑棋)
//dx、dy共同决定一个移动的方向
int dx[] = {-1,-1,-1, 1,1,1,0, 0}; //表示第i行,所以-1表示向上 
int dy[] = {-1, 0, 1,-1,0,1,1,-1}; //表示第j列,所以-1表示向左 

/*
	求出位于(x, y)位置的a类型棋子的可行位置;b类型是a类型的对手。
	求可行位置的思路是: 
		判断a类型棋子的上下左右及斜对角线上是否有b类型的棋子
		如果有的话,再继续沿相同的方向走进行判断 
		直至遇到空白位或遇到了a类型棋子
		如果是前者,则这个位置是一个合法操作,打印出来
		如果是后者,则这个位置不可行 		
*/
void is(int a, char b, int x, int y)//
{
    for(int k=0; k<8; k++)
    {
        int tx = x+dx[k];
        int ty = y+dy[k];
        int num = 0;
        while(tx>=0 && ty>=0 && tx<8 && ty<8 && mp[tx][ty] == b)
        {
            tx += dx[k];
            ty += dy[k];
            ++num;
        }
        if(num>=1 && mp[tx][ty] == '-')
            f[a][tx][ty] |= 1<<k; //利用位运算同时保存k,即可行的移动方向,便于后面落子时直接定向翻转。
    }
}

void init() //获得黑白子可执行操作的点
{
    for(int i=0; i<8; i++)
        for(int j=0; j<8; j++)
            if(mp[i][j] == 'W')
                is(0, 'B', i, j);
            else if(mp[i][j] == 'B')
                is(1, 'W', i, j);
}

bool print(bool isprint, int p)
//p为当前操作者,isprint表示是否输出,便于将此函数【输出可行位置】【查询是否有可行位置】两用
{
    bool flag = false;
    for(int i=0; i<8; i++)
        for(int j=0; j<8; j++)
            if(f[p][i][j])
            {
                if(flag && isprint)
                    cout<<" ";
                flag = true;
                if(isprint)
                    cout<<'('<<i+1<<','<<j+1<<")";
                else
                    return flag;
            }
    if(isprint)
    {
        if(flag)
            cout<<endl;
        else
            cout<<"No legal move."<<endl;
    }
    return flag;
}

void move(int x, int y, int p)
{
    char ch;
    if(p == 0)
        ch = 'W';
    else
        ch = 'B';
    mp[x][y] = ch;
    for(int i=0; i<8; i++)//表示八个移动方向 
    {
    	/*			 
			因为f[p][x][y]保存了(x, y)位置时可行的移动方向,
			这一步实际上是为了找出当时保存的那个可以动方向的k值.
			&:按位与.
			f[p][x][y] 和 (1 << i) 只有在完全相同时,结果才会为true 
		*/
        if(f[p][x][y] & (1 << i))
        {
            int tx = x-dx[i];//为什么是减不是加呢?嘿嘿 (因为要往回退,与is()函数的操作刚好相反)
            int ty = y-dy[i];
            while(tx>=0 && ty>=0 && tx<8 && ty<8 && mp[tx][ty] != ch)
            {
                mp[tx][ty] = ch;
                tx -= dx[i];
                ty -= dy[i];
            }
        }
    }
}

int main()
{
    int n;
    cin >> n; //输入游戏局数 
    while(n--)
    {
        memset(mp, 0, sizeof(mp));
        for(int i =0 ; i < 8; i++) //输入棋盘 
            cin >> mp[i]; //每次接收一行 
            
        char ch[5]; //ch既用来保存输入的黑/白棋, 也用来保存输入的指令...
        
        cin >> ch;
		int p; //输入的黑白棋:ch(W/B) ----> p(0/1)
        if(ch[0] == 'W')
            p = 0;
        else
            p = 1;
            
        while(ch[0] != 'Q')
        {
        	//每次都提前先求出黑棋和白棋可行的位置保存下来!
            memset(f, 0, sizeof(f));
            init();
            //输入指令 
            cin >> ch;
            if(ch[0] == 'L')
                print(true, p); //打印可行棋子 
            else if(ch[0] == 'M')
            {
                if(!print(false, p)) //判断是否存在可行棋子 
                    p ^= 1;	//更换玩家 - 使用异或运算符(不同时为1,相同时为0) 
                move(ch[1]-'0'-1, ch[2]-'0'-1, p);//从指令中获取坐标(ch[1]-'0'-1, ch[2]-'0'-1) 
                //更换玩家,同时计算此时黑白棋子总数并输出 
                p ^= 1;
                int wnum = 0;
                int bnum = 0;
                for(int i=0;i<8;i++)
                    for(int j=0;j<8;j++)
                    {
                        if(mp[i][j] == 'W')
                            ++wnum;
                        if(mp[i][j] == 'B')
                            ++bnum;
                    }
                printf("Black - %2d White - %2d\n", bnum, wnum);
             }
         }
        //输出当前棋盘 
        for(int i=0; i<8; i++)
            cout << mp[i] << endl;
        if(n != 0)
            cout << endl;
    }
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值