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");elseprintf("YES\n");cate.clear();pos.clear();}return 0;
}

UVA 220 黑白棋

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

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<<")";elsereturn flag;}if(isprint){if(flag)cout<<endl;elsecout<<"No legal move."<<endl;}return flag;
}void move(int x, int y, int p)
{char ch;if(p == 0)ch = 'W';elsech = '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;elsep = 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;
}

UVA1589 象棋 + UVA 220 黑白棋相关推荐

  1. UVa 220 黑白棋 算法竞赛入门经典 习题4-3

    输出格式输出格式输出格式!UVaOJ的输出控制让我想起了玩黑魂时的感觉. 这道题难点在M操作后的输出 "Black - " 一眼看去'-'后面是两个个空格,然而事实是-与后面的数字 ...

  2. C++程设实验项目三:黑白棋与基于UCT算法的AI

    在这篇博客里,我将总结一下在这次实验中学到的UCT算法实现原理. 首先是参考文章: https://blog.csdn.net/u014397729/article/details/27366363 ...

  3. 吴昊品游戏核心算法 Round 9 —— 正统黑白棋AI(博弈树)

    黑白棋程式简史 在1980年代,电脑并不普及,在黑白棋界里,最强的仍然是棋手(人类). 到了1990年代初,电脑的速度以几何级数增长,写出来的黑白棋程式虽然仍然有点笨拙,但由于计算深度(电脑的速度快) ...

  4. 吴昊品游戏核心算法 Round 9 —— 黑白棋AI系列之西洋跳棋(第二弹)(双向BFS+STL)(POJ 1198)...

    接上回,如图所示,这是黑白棋的一个变种,Solitaire也是一种在智能手机上普遍存在的一种游戏.和翻转棋(Flip Game)一样,西洋跳棋(Solitaire)也没有正统的黑白棋(奥赛罗,又称Ot ...

  5. uva 220 - Othello(黑白翻转棋)

    习题4-3 黑白棋(Othello, ACM/ICPC World Finals 1992, UVa220) 你的任务是模拟黑白棋游戏的进程.黑白棋的规则为:黑白双方轮流放棋子,每次必须 让新放的棋子 ...

  6. c++课程设计——黑白棋(QT实现+minmax算法实现ai版)

    关联:NEU 东北大学 本篇将大致描述基础思路,后续会添加相应的演示视频和整个工程文件 首先我将大致描述一下我的创作进度 1.先完成棋盘的绘制(QT仅绘制棋盘) mainwindow.h #ifnde ...

  7. Windows游戏设计(三)- 黑白棋游戏 - 使用Win32 SDK

    注:以下程序为本人原创,写的不好,若有好的建议,望留言告知.而若能帮助一二访客,幸甚! 上回用Python 写黑白棋,后来想添加个最小最大规则搜索博弈树的算法,没能实现,于是想先用Win32 写一个, ...

  8. 51nod 1368:黑白棋 二分图最大匹配

    1368 黑白棋 题目来源: TopCoder 基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题  收藏  取消关注 有一个N*M的棋盘(1<=N,M< ...

  9. 《算法竞赛入门经典》习题4-3 黑白棋(Othello, ACM、ICPC World Finals 1992, UVa220)

    原题及翻译 Othello is a game played by two people on an 8 x 8 board, using disks that are white on one si ...

最新文章

  1. IT服务台来电分配技术——ACD介绍
  2. 当我们使用锁同步代码的时候,会在什么时候释放锁呢
  3. Elasticsearch使用BulkProcessor批量插入
  4. ssh报错:Could not load host key:/etc/ssh/ssh_host_rsa_keyssh_host_ecdsa_keyssh_host_ed25519_key...
  5. 搜索引擎索引之如何建立索引
  6. Notepad++远程连接Linux系统
  7. python中的*args和**kwargs(* 与 **)
  8. 博文视点OpenParty第11期“世界黑客大会那些事儿”成功举办
  9. 易到高管被原百度外卖CEO巩振兵逼下跪:职场人到中年的无奈
  10. 【译】R包介绍:Online Random Forest
  11. Centos7安装整合Apache+PHP,安装nginx后nginx无法解析.php文件
  12. 传奇3单机服务器怎么修改器,自己是GM并架设了传奇3单机版,如何改变装备属性?...
  13. 华硕主板实现Wake on lan 网络唤醒的种种细节
  14. umoocs外语慕课答案_umoocs答案怎么查,中国高校外语慕课平台(UMOOCs)答案公众号
  15. 安防视频监控系统设计
  16. 抖音推荐算法原理全文详解
  17. python 3d绘图模块_Python绘制3D图形
  18. win10+Python3.7.3+OpenCV3.4.1入门学习(十二 图像轮廓)————12.4 Hu矩
  19. Jmeter+Jenkins+Ant 接口自动化持续集成框架
  20. 数据库-[mysql]--详细笔记+教程

热门文章

  1. 小学六年级上册计算机教学总结,小学信息技术五年级上册教学工作总结
  2. python调用htk工具箱_HTK学习笔记(一)在win32(win7)下安装HTK详细体验教程
  3. 智慧城市建设热潮下怎么抢占先机?
  4. 摄像头参数介绍 ———— 分辨率
  5. android 暴风影音目录,暴风影音Android正式版发布!详细评测
  6. 普通类,抽象类和接口之间的区别
  7. 2021年美容师(初级)考试总结及美容师(初级)考试试题
  8. mac上的PowerDesigner(PDM)
  9. redis缓存失效时间设为多少_Redis有效时间设置及时间过期处理
  10. 桥接模式和中继模式的区别