题目描述

在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步.

黑白双方交替走棋任意一方可以先走如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局。

输入输出格式

输入格式:

从文件中读入一个4*4的初始棋局,黑棋子用B表示,白棋子用W表示,空格地带用O表示。

输出格式:

用最少的步数移动到目标棋局的步数。

输入输出样例:

输入:

BWBO

WBWB

BWBW

WBWO

输出:
5

分析:
先讲讲我做这道题的悲惨经历,因为我太弱了想了半小时然后写一下卡一下的写出个BFS然后果不其然样例都没过,

然后换了种思路重写了一遍然后在机房3位dalao历经1小时的debug最后终于A了这道题,十分感谢这3位dalao.

接下来进入正题.

思路:
首先这是一道搜索加模拟题这道题是真的要我这个蒟蒻的命,先分析题目由得知我们有一个4*4的棋盘其中有两个字母为“O”的空白

然后在棋盘中其他字母为B(Black)或W(White),然后我们需要像走五子棋一样要么先走白要么先走黑当出现横排出现4格连续的“B”或“W或”竖列出现4格连续的“B”或“W”

亦或是斜着(包含左斜,右斜)出现4格连续的“B”或“W”只要出现其中一种情况就达成了目标情况(就像五子棋一样).

输出最小步数.

方法:
因为是最小步数我们首先考虑BFS显然这道题BFS是行的通的,但是有一点需要注意玩过五子棋都知道黑白谁先手结果可能不一样,因此我们需要跑两边BFS(1.白先手时2.黑先手时)

然后我们需要下一个棋盘的状况,我们可以用到和“八数码难题”一样的更新操作即我们可以先将每个矩阵压成一个字符串当需要判断是否成立目标状况时在变回矩阵判断,

压缩操作:

为什么要这样做,因为每次Map都在变而我们必须要存下每个不同的状况所以我们需要字符串存下每个不同状况.

void compression()
{
change.clear();//在压缩成一个字符串时当然需要将这个字符串清空
for(int i=;i<=;i++)//4*4的矩阵
for(int j=;j<=;j++)
change.push_back(Map[i][j]);
}

既然有压缩操作那就必须有拆分的操作

拆分操作:
为什么要这么做,由上面的压缩操作可以得到一个全新的棋盘状况但是我们需要判断当前的状况是否符合标准状况的条件但是很显然字符串是无法判断的

所以我们需要将这个字符串再拆分成一个全新的矩阵再用这个矩阵判断是否符合情况

void break_up(string a)
{
posx=;posy=;
for(int i=;i<a.length();i++)
{
if(posy==)
{
posy=;
posx++;
}
Map[posx][posy]=a[i];
posy++;
}
}

上面都提到了判断操作

相信判断操作大家都会这里就不多做解释了

判断操作:

当符合情况就返回true否则就返回false

可以发现check里我们用的是string w 那就说明再判断前我们一定要将需要判断的字符串拆分成矩阵

bool check(string w)
{
for(int i=;i<=;i++)//横竖
{
if(Map[i][]==Map[i][]&&Map[i][]==Map[i][]&&Map[i][]==Map[i][]) return true;
if(Map[][i]==Map[][i]&&Map[][i]==Map[][i]&&Map[][i]==Map[][i]) return true;
}
if(Map[][]==Map[][]&&Map[][]==Map[][]&&Map[][]==Map[][]) return true;//右斜
if(Map[][]==Map[][]&&Map[][]==Map[][]&&Map[][]==Map[][]) return true;//左斜
return false;
}

在贴整段代码之前我还要再BB一两句下面这些话可能对你理解这题的BFS有帮助

1.由题意我们可以知道这题的BFS其实就是将“O”这个点去更新整个棋盘的所有点,我用的是swap这个就是将两个点交换位置至于BFS怎么写看大家的习惯

但是要用swap的小伙伴要注意一下,当搜索不满足情况时因为我们已经交换这两个点所以我们必须再交换回去才能continue(即再swap一次)

2.有题意我们可以得知这题有两个空格“O”谁先走都没有规定因此我们必须写两个for循环这里也许需要思考一下为什么.

3.再有题意可得这是一个像五子棋一样的游戏所以我们必须一黑一白交替走棋(至于谁先我前面已经说了我们要注意这两种情况因为情况可能不同)

由此三点我们可以得出我们的结构体里需要存储的东西

struct Node
{
int x1,y1,x2,y2,t;//x1,y1为第一个空格“O”的位置 x2,y2为第二个“O”的位置
bool order;//借此判断走棋顺序
string s;//存储每一个矩阵压成的字符串
};

接下来我会贴出整段代码:
如果还有点懵的可以看看整段代码让你的思路更清晰

代码:

#include <iostream>
#include <cstring>
#include <cmath>
#include <string>
#include <queue>
#include <map>
using namespace std;
const int INF=0x3f3f3f3f;
char Map[][],tmp[][];
int startx1,starty1,startx2,starty2,posx,posy,ans=INF;
map<string,bool> vis;
string x,change;
bool flag;
int dirx[]={,,-,};
int diry[]={,,,-};
struct Node
{
int x1,y1,x2,y2,t;//这些变量名我在上面解释的很清楚
bool order;
string s;
};
void compression()//压缩操作
{
change.clear();
for(int i=;i<=;i++)
for(int j=;j<=;j++)
change.push_back(Map[i][j]);
}
void break_up(string a)//拆分操作
{
posx=;posy=;
for(int i=;i<a.length();i++)
{
if(posy==)
{
posy=;
posx++;
}
Map[posx][posy]=a[i];
posy++;
}
}
bool check(string w)//判断是否成立
{
for(int i=;i<=;i++)
{
if(Map[i][]==Map[i][]&&Map[i][]==Map[i][]&&Map[i][]==Map[i][]) return true;
if(Map[][i]==Map[][i]&&Map[][i]==Map[][i]&&Map[][i]==Map[][i]) return true;
}
if(Map[][]==Map[][]&&Map[][]==Map[][]&&Map[][]==Map[][]) return true;
if(Map[][]==Map[][]&&Map[][]==Map[][]&&Map[][]==Map[][]) return true;
return false;
}
void bfs(bool c)
{
vis[x]=true;
queue<struct Node> que;
while(!que.empty())//因为我们要进行第二次BFS所以有可能第一次的BFS的队列并没有弹空
{//所以进行第二次BFS时我们必须将队列弹空
que.pop();
}
struct Node now;
now.x1=startx1; now.y1=starty1; now.x2=startx2; now.y2=starty2; now.t=; now.s=x; now.order=c;
que.push(now);
while(!que.empty())
{
now=que.front(); break_up(now.s);//更新矩阵
/*
注意这个时候虽然我们的字符串更新了
但是我们的矩阵并未更新,如果不更新矩阵就判断是否成立的话那我们判断的就是上一个矩阵的状态这毫无疑问是错误的
*/
if(check(now.s))//判断矩阵而不是字符串,就如上面说的一样
{
ans=min(ans,now.t);
return;
} que.pop();
for(int i=;i<;i++)
{
int xx=now.x1+dirx[i];
int yy=now.y1+diry[i];
if(xx<||xx>||yy<||yy>) continue;
swap(Map[now.x1][now.y1],Map[xx][yy]);
if(now.order&&Map[now.x1][now.y1]=='B')//该走白子时走黑子的情况 这是不合法的
{
swap(Map[xx][yy],Map[now.x1][now.y1]);//不符合情况时我们必须swap回到上一个状态
continue;
}
if(!now.order&&Map[now.x1][now.y1]=='W')//该走黑子时走白子的情况 这是不合法的
{
swap(Map[xx][yy],Map[now.x1][now.y1]);//不符合情况时我们必须swap回到上一个状态
continue;
}
compression();//更新字符串(将更新的矩阵再压缩成一个新的字符串)
if(vis[change])//判断这个字符串是否用过
{
swap(Map[xx][yy],Map[now.x1][now.y1]);//我前面应该讲了swap当不符合情况时我们必须swap回到上一个状态
continue;
}
vis[change]=true;
swap(Map[xx][yy],Map[now.x1][now.y1]);//当然需要swap回到上一个情况因为我们必须保证更新这个矩阵所有能行的情况时这个矩阵的Map不能改变
struct Node next;
next.x1=xx;next.y1=yy;next.t=now.t+;next.s=change;next.order=!now.order;next.x2=now.x2;next.y2=now.y2;//next.order=!now.order因为下次必须走和这次颜色不同的棋子
que.push(next); //next.x2=now.x2 next.y2=now.y2 因为这里是第一个空格的情况所以第二个空格的坐标并未改变
}
for(int i=;i<;i++)
{
int xx=dirx[i]+now.x2;
int yy=diry[i]+now.y2;
if(xx<||xx>||yy<||yy>) continue;
swap(Map[now.x2][now.y2],Map[xx][yy]);
if(now.order&&Map[now.x2][now.y2]=='B')//该走白子时走黑子的情况 这是不合法的
{
swap(Map[xx][yy],Map[now.x2][now.y2]);//不符合情况时我们必须swap回到上一个状态
continue;
}
if(!now.order&&Map[now.x2][now.y2]=='W')//该走黑子时走白子的情况 这是不合法的
{
swap(Map[xx][yy],Map[now.x2][now.y2]);//不符合情况时我们必须swap回到上一个状态
continue;
}
compression();//更新字符串(将更新的矩阵再压缩成一个新的字符串)
if(vis[change])//判断这个字符串是否用过
{
swap(Map[xx][yy],Map[now.x2][now.y2]);//我前面应该讲了swap当不符合情况时我们必须swap回到上一个状态
continue;
}
vis[change]=true;
swap(Map[xx][yy],Map[now.x2][now.y2]);//当然需要swap回到上一个情况因为我们必须保证更新这个矩阵所有能行的情况时这个矩阵的Map不能改变
struct Node next;
next.x2=xx;next.y2=yy;next.t=now.t+;next.order=!now.order;next.s=change;next.x1=now.x1;next.y1=now.y1;//next.order=!now.order因为下次必须走和这次颜色不同的棋子
que.push(next);//next.x1=now.x1 next.y1=now.y1 因为这里是第二个空格的情况所以第一个空格的坐标并未改变
}
}
return;
}
int main()
{
for(int i=;i<=;i++)
for(int j=;j<=;j++)
cin>>Map[i][j];
for(int i=;i<=;i++)//我们需要字符串x保存最原始的棋盘因为我们要跑两次BFS白棋先走和黑棋先走的两种情况
for(int j=;j<=;j++)
x.push_back(Map[i][j]);
for(int i=;i<=;i++)//找到两个空格的坐标
{
for(int j=;j<=;j++)
{
if(Map[i][j]=='O'&&!flag)
{
startx1=i;starty1=j;//startx1 starty1 第一个空格
flag=true;
}
else if(Map[i][j]=='O'&&flag)
{
startx2=i;starty2=j;//startx2 starty2 第二个空格
}
}
}
bfs(true);//true 是白棋先走的情况
bfs(false);//false 是黑棋先走的情况
if(ans==)//这里为当我们输入的是符合情况
cout<<;//我用的luogu的oj不知道为什么当输入的是符合情况时答案是1所以我们这里特判一下就可以了
else
cout<<ans;
return ;
}

洛谷P2346四子连棋的更多相关文章

  1. P2346 四子连棋

    P2346 四子连棋 迭代加深++ 题意描述 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋 ...

  2. 【洛谷 P2346】四子连棋(状态压缩,搜索)

    其实这题可以直接二进制状压做,1表示黑棋,0表示白棋,另外记录下2个空点的位置就行了. 具体看代码(冗长): #include <iostream> #include <cstdio ...

  3. codevs1004四子连棋[BFS 哈希]

    1004 四子连棋   时间限制: 1 s   空间限制: 128000 KB   题目等级 : 黄金 Gold   题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗 ...

  4. Codevs p1004 四子连棋

                          四子连棋 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向 ...

  5. 【宽度优先搜索】神奇的状态压缩 CodeVs1004四子连棋

    一.写在前面 其实这是一道大水题,而且还出在了数据最水的OJ上,所以实际上这题并没有什么难度.博主写这篇blog主要是想写下一个想法--状态压缩.状态压缩在记录.修改状态以及判重去重等方面有着极高的( ...

  6. 洛谷P4363 一双木棋 chess

    洛谷P4363 一双木棋 chess 省选最水的一道题了. 且看我数个月AC一道题...... 具体是这样的:我们发现这个下了棋的地方一定形成一个锯齿形,那么怎么状态压缩呢? 维护轮廓线! 从左下角出 ...

  7. codevs 1004 四子连棋

    1004 四子连棋  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold     题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白 ...

  8. codevs 1004 四子连棋 BFS、hash判重

    004 四子连棋 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold       题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋 ...

  9. 迭代加深搜索[codevs1004 四子连棋]

    迭代加深搜索 一.算法简介 迭代加深搜索是在速度上接近广度优先搜索,空间上和深度优先搜索相当的搜索方式.由于在使用过程中引入了深度优先搜索,所以也可以当作深度优先搜索的优化方案. 迭代加深搜索适用于当 ...

随机推荐

  1. 01_常用 Linux 命令的基本使用

    01. 学习 Linux 终端命令的原因 Linux 刚面世时并没有图形界面,所有的操作全靠命令完成,如 磁盘操作.文件存取.目录操作.进程管理.文件权限 设定等 在职场中,大量的 服务器维护工作 都 ...

  2. HTML中id和class选择器

    <1>.id和class的区别? id相当于人的身份证不可以重复 class相当于人的名称可以重复 一个HTML标签只能绑定一个id名称 一个HTML标签可以绑定多个class名称 < ...

  3. Java 的标识接口作用

    原文地址:标识接口 作用作者:feisong 时间:2019-01-2315:49:35 标识接口是没有任何方法和属性的接口.标识接口不对实现它的类有任何语义上的要求,它仅仅表明实现它的类属于一个特定 ...

  4. Linux系统运维基础测试题

    1    Linux运维基础测试题(第一关) 通过这段时间学习Linux基础命令,为了检测自己对Linux基础命令掌握的情况,从网上整理13到测试题,并将其整理出来供大家参考学习. 1.1    习题 ...

  5. loushang框架的开发中关于BSP的使用,将写好的功能模块部署到主页界面结构上

    前言: 当我们已经开发好相应的模块或者功能的时候,需要将这个功能部署在index主页上作为可点击直接使用的模块,而不是每次需要去浏览对应的url地址. 这时候就需要运用到L5的BSP. 作为刚刚入门l ...

  6. js中面向对象(创建对象的几种方式)

    1.面向对象编程(OOP)的特点: 抽象:抓住核心问题 封装:只能通过对象来访问方法 继承:从已有的对象下继承出新的对象 多态:多对象的不同形态 一.创建对象的几种方式 javascript 创建对象 ...

  7. 【Linux】Nginx无法加载.woff .eot .svg .ttf问题解决

    只需要修改Nginx的vhosts.ini,加上以下代码即可修复该问题 location ~ \.(eot|otf|ttf|woff|woff2|svg)$ { add_header Access-C ...

  8. MongoDB在单机上搭建分片副本集群(windows),版本二

    配置可以参考前面一篇 https://www.cnblogs.com/a-horse-mosaic/p/9284010.html 副本集是一组服务器,其中有一个主服务器(primary),用于处理客户 ...

  9. yum方式安装及配置最新的mysql5.7

    1.删除旧版本的MySQL rpm -qa|grep -i mysql 用命令yum -y remove mysql 2.下载新版安装源 下载mysql的repo源 这个安装的mysql5.7.20 ...

  10. Apache Tomcat 整合

    Infi-chu: http://www.cnblogs.com/Infi-chu/ 一.Apache+Tomcat整合是什么: 1.Apache默认访问端口是80,Tomcat默认访问端口是8080 ...