题目描述

在一个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. jquery插件layer

    //信息框-例1 layer.alert('见到你真的很高兴', {icon: 6}); //信息框-例2 layer.confirm('你确定你很帅么?', {icon: 3}, function( ...

  2. AngularJS 控制器函数

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  3. C语言输出格式

    1 一般格式    printf(格式控制,输出表列)    例如:printf("i=%d,ch=%c\n",i,ch);    说明:    (1)“格式控制”是用双撇号括起来 ...

  4. oracle 的分页查询与mysql 的分页查询

    oracle 的分页查询: select * from (select o.*,rownum rn  from Persons o) where rn >40 and rn <=60 : ...

  5. Flask—04-文件上传与邮件发送(自带优化)

    文件上传与邮件发送 可以按照标题分别直接粘贴对应的文件夹,运行直接用: 原生上传 模板文件 <form method="post" enctype="multipa ...

  6. Xcode 9.0 报错, Safe Area Layout Guide Before IOS 9.0

    Xcode 9.0 新建工程报错 xcode Safe Area Layout Guide Before IOS 9.0 如下图,在Builds for 选择iOS9.0 and Later,不勾选U ...

  7. 请对比 Exception 和 Error,另外,运行时异常与一般异常有什么区别?

    error指的是不可预料的错误,可能会导致程序宕机:而exception指的是在程序运行中可以预见的异常,而异常分为检查异常与一般异常,检查异常需要在程序中显示捕获并处理,一般异常可以通过程序编码来进 ...

  8. rest_framework -- mixins&generics

    上面的mixins.generics都是rest_framework里的模块,我们可以继承其中的某些类,达到代码量减少的效果,这里充分体现出了面向对象的继承 一.mixins模块 mixins : f ...

  9. union的两个子查询是否并行

    需求描述 问题:subquery 1 union subquery2,其中union左右的两个子查询是否并行. 场景:业务中性能敏感的业务,希望能加快速度,如果数据库能两个子查询并行执行,既可以节省时 ...

  10. TiDB集群手动安装

    TIDB的安装 TiDB 是 PingCAP 公司受 Google Spanner / F1 论文启发而设计的开源分布式 HTAP (Hybrid Transactional and Analytic ...