用BFS和DFS解决圆盘状态搜索问题
人工智能课程的实验(我的解法其实更像是算法课程的实验)
用到的算法:深度优先搜索、宽度优先搜索(状态扩展的不同策略)
数据结构:表示状态的结构体、多维数组
(可能是最近做算法竞赛题的影响,这次并不像以前那样依赖类和面向对象了,而是用最简单(几乎没有封装)的数据表示方法和大量的全局变量来存储数据,用面向过程的写法,以快速解决某一问题为目的设计程序。安全性和可扩展性势必降低,有些技巧的使用也让代码变得难懂;但是代码简洁,节省运行的时间和空间开销,这应该就是算法竞赛更加看重的吧)
这次用了C++写了控制台版,打印状态三元组;又用C#写了图形界面版,打印出圆盘的状态。
二者的算法是完全一致的,执行结果也一致,只是语法和输出的一些处理不同。
下面是运行结果截图,以深度优先为例

下面以C++版为例介绍实现
结构体和全局变量的定义
struct State
{
int a, b, c;//表示圆盘朝南方向的数字
State(){}
State(int na, int nb, int nc) :a(na), b(nb), c(nc){}
}; int vis[][][];//用来记录每个状态是否被访问过
int num;//用来记录第几个状态
DFS(用栈实现)
int dfs()
{
stack<State> sta;
sta.push(State(,,));
vis[][][] = ;
while (sta.size())
{
State cur = sta.top();
sta.pop();
cout << ++num << ". ("<<cur.a<<","<<cur.b<<","<<cur.c<<")"<<endl;
if (cur.a == && cur.b == && cur.c == )
return ;//命中
if (cur.c - > && vis[cur.a][cur.b][cur.c-] == )
{
vis[cur.a][cur.b][cur.c-] = ;
sta.push(State(cur.a, cur.b, cur.c-));
}
if (cur.b - > && vis[cur.a][cur.b-][cur.c] == )
{
vis[cur.a][cur.b-][cur.c] = ;
sta.push(State(cur.a, cur.b-, cur.c));
}
if (cur.a - > && vis[cur.a-][cur.b][cur.c] == )
{
vis[cur.a-][cur.b][cur.c] = ;
sta.push(State(cur.a-, cur.b, cur.c));
}
}
return ;
}
BFS(用队列实现)
int bfs()
{
queue<State> que;
que.push(State(, , ));
vis[][][] = ;
while (que.size())
{
State cur = que.front();
que.pop(); cout << ++num << ". ("<<cur.a<<","<<cur.b<<","<<cur.c<<")"<<endl;
if (cur.a == && cur.b == && cur.c == )
return ;//命中
if (cur.a - > && vis[cur.a - ][cur.b][cur.c] == )
{
vis[cur.a-][cur.b][cur.c] = ;
que.push(State(cur.a-, cur.b, cur.c));
}
if (cur.b - > && vis[cur.a][cur.b - ][cur.c] == )
{
vis[cur.a][cur.b-][cur.c] = ;
que.push(State(cur.a, cur.b-, cur.c));
}
if (cur.c - > && vis[cur.a][cur.b][cur.c - ] == )
{
vis[cur.a][cur.b][cur.c-] = ;
que.push(State(cur.a, cur.b, cur.c-));
}
}
return ;
}
你会发现DFS和BFS的不同仅在于对某一状态(节点)进行扩展时,其兄弟结点被暂存的顺序。这个顺序决定了节点扩展的顺序,即将“树”这样的“半线性结构”转化为“线性结构”的不同策略。
因为这两种搜索策略都是从一开始就知道要对每一个未命中的节点进行怎样的处理,即搜索过程中产生的结果对搜索策略没有影响,所以这两种都属于“盲目式搜索”。相对而言,人工智能领域发展出“启发式搜索”,即在进行待扩展节点的选取时,要根据一个“估价函数”来选取“最有希望”的节点分支进行下一步扩展。
老师的习题解析中对DFS进行了改进,在扩展某个节点时判断的是它的子节点,这样可以尽早找到目标。个人认为这相当于在局部做了广度优先。
改进后的访问过的状态由17个降到5个,但其实加上判断过又未命中的兄弟节点,是降到了11个。如下

改进版DFS的代码
int dfs_a()
{
stack<State> sta;
sta.push(State(,,));
vis[][][] = ;
while (sta.size())
{
State cur = sta.top();
sta.pop();
cout << ++num << ". ("<<cur.a<<","<<cur.b<<","<<cur.c<<")"<<endl; if (cur.c - > && vis[cur.a][cur.b][cur.c-] == )
{
vis[cur.a][cur.b][cur.c-] = ;
if (cur.a == && cur.b == && cur.c- == )
{
cout << ++num << ". ("<<cur.a<<","<<cur.b<<","<<cur.c-<<")"<<endl;
return ;//命中
}
sta.push(State(cur.a, cur.b, cur.c-));
}
if (cur.b - > && vis[cur.a][cur.b-][cur.c] == )
{
vis[cur.a][cur.b-][cur.c] = ;
if (cur.a == && cur.b- == && cur.c == )
{
cout << ++num << ". ("<<cur.a<<","<<cur.b-<<","<<cur.c<<")"<<endl;
return ;//命中
}
sta.push(State(cur.a, cur.b-, cur.c));
}
if (cur.a - > && vis[cur.a-][cur.b][cur.c] == )
{
vis[cur.a-][cur.b][cur.c] = ;
if (cur.a- == && cur.b == && cur.c == )
{
cout << ++num << ". ("<<cur.a-<<","<<cur.b<<","<<cur.c<<")"<<endl;
return ;//命中
}
sta.push(State(cur.a-, cur.b, cur.c));
}
}
return ;
}
以上就是算法的内容。
关于C#图形界面的实现,也很简单。下面是显示每个圆盘状态的函数
//打印状态函数
private void print_circle(State st,int num)
{
Thread.Sleep();
Graphics gra = this.groupBox1.Controls[num-].CreateGraphics();//指定第num个圆盘显示 gra.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; Pen pen = new Pen(Color.Black);//画笔颜色 gra.DrawEllipse(pen, , , , );//画圆的方法,x坐标、y坐标、宽、高
gra.DrawEllipse(pen, , , , );
gra.DrawEllipse(pen, , , , ); Font font1 = new Font("", , FontStyle.Bold);
Font font2 = new Font("", , FontStyle.Bold);
Brush bush = new SolidBrush(Color.Red);//字填充的颜色
gra.DrawString(num + "", font2, bush, , );
gra.DrawString("A", font1, bush, , );
gra.DrawString("B", font1, bush, , );
gra.DrawString("C", font1, bush, , );
gra.DrawString(st.a+"", font1, bush, , );
gra.DrawString(st.b+"", font1, bush, , );
gra.DrawString(st.c+"", font1, bush, , );
}
关于界面的设计,预先放好足够多的picturebox,将它们放入一个groupbox里,然后根据传递进来的参数找特定的picturebox画图。

在遍历groupbox时,我发现里面元素的顺序如果不是按顺序拖入的,会非常错乱。。。由于很菜鸟,我百度了很久都没有找到直接修改内部顺序的方法,只好打开groupbox的定义代码,调整添加元素函数的顺序才得以重排。
以上是我对这个圆盘问题的求解。
在检查实验时,老师说这个问题更通用的抽象是一个5个参数的“搜索机”,5个参数分别为:初始状态集合、目标状态集合、初始状态、目标状态、状态转移函数。(百度后发现好像图灵机的模型)这种方法需要在一开始生成所有可能状态。相比我的“动态生成状态”策略,这样势必会增加空间开销,但在搜索次数多时可以节省重复生成状态的时间,两种方法各有千秋吧。后一种方法更切合现在学习人工智能课的“状态空间搜索”思想。
用BFS和DFS解决圆盘状态搜索问题的更多相关文章
- BFS 、DFS 解决迷宫入门问题
问题 B: 逃离迷宫二 时间限制: 1 Sec 内存限制: 128 MB提交: 12 解决: 5[提交][状态][讨论版] 题目描述 王子深爱着公主.但是一天,公主被妖怪抓走了,并且被关到了迷宫. ...
- 用深度优先搜索(DFS)解决多数图论问题
前言 本文大概是作者对图论大部分内容的分析和总结吧,\(\text{OI}\)和语文能力有限,且部分说明和推导可能有错误和不足,希望能指出. 创作本文是为了提供彼此学习交流的机会,也算是作者在忙碌的中 ...
- 深度优先搜索(DFS)与广度优先搜索(BFS)的Java实现
1.基础部分 在图中实现最基本的操作之一就是搜索从一个指定顶点可以到达哪些顶点,比如从武汉出发的高铁可以到达哪些城市,一些城市可以直达,一些城市不能直达.现在有一份全国高铁模拟图,要从某个城市(顶点) ...
- 搜索分析(DFS、BFS、递归、记忆化搜索)
搜索分析(DFS.BFS.递归.记忆化搜索) 1.线性查找 在数组a[]={0,1,2,3,4,5,6,7,8,9,10}中查找1这个元素. (1)普通搜索方法,一个循环从0到10搜索,这里略. (2 ...
- 搜索(BFS、DFS、回溯)
这类题是最简单的了都是一个套路,不像动态规划一类题一个套路,没做过就是不会也极难想出来. 一.BFS 解决的问题:用来初始点解决到指定点的最短路径问题,因为图的每一层上的点到初始点的距离相同.(注意是 ...
- bfs和dfs辨析—基础复习(从stack和queue的角度来理解区别,加深理解,不再模糊)
参考: https://www.cnblogs.com/Tovi/articles/6194815.html https://blog.csdn.net/dangzhangjing97/article ...
- BFS和DFS详解
BFS和DFS详解以及java实现 前言 图在算法世界中的重要地位是不言而喻的,曾经看到一篇Google的工程师写的一篇<Get that job at Google!>文章中说到面试官问 ...
- 通俗理解BFS和DFS,附基本模板
1.BFS(宽度优先搜索):使用队列来保存未被检测的节点,按照宽度优先的顺序被访问和进出队列 打个比方:(1)类似于树的按层次遍历 (2)你的眼镜掉在了地上,你趴在地上,你总是先摸离你最近的地方,如果 ...
- ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 A、Saving Tang Monk II 【状态搜索】
任意门:http://hihocoder.com/problemset/problem/1828 Saving Tang Monk II 时间限制:1000ms 单点时限:1000ms 内存限制:25 ...
随机推荐
- Hello,world,l'm coming!
#include<studio.h> int main() { printf("Hello,Word!" l'm coming\n"); return0; }
- 设置UIScrollView只可以水平或者竖直滚动
UIScrollView里边包含多个UIWebView: 可以通过设置contentSize的值,设置其width为UIScrollerView可视区域的宽度:即UIScrollView的width, ...
- Xcode7真机测试
根据这个网址上的步骤能够完成真机测试,我已经试过了,还不错 http://www.bubuko.com/infodetail-1061938.html
- 程序员实用的 MySQL sql 语句
这儿只讲究实用, 程序员编程时常用到的 MySQL的 sql语句(不包括基本的 select, update, delete 等语句). 1. 添加一个用户build,并赋予所有权限的命令 gran ...
- UE4学习笔记(三): 为什么使用C++替代UnrealScript?
原文链接: https://forums.unrealengine.com/showthread.php?2574-Why-C-for-Unreal-4&p=16252&viewful ...
- Lenovo k860i 移植Android 4.4 cm11进度记录【下篇--实时更新中】
2014.8.24 k860i的cm11的移植在中断了近两三个月之后又開始继续了,进度记录的日志上一篇已经没什么写的了,就完结掉它吧,又一次开一篇日志做下篇好了.近期的战况是,在scue同学的努力之下 ...
- Android 属性动画(一)
1.概述 Android提供了几种动画类型:View Animation .Drawable Animation .Property Animation .View Animation相当简单,不过只 ...
- Android Studio试用总结
Android Studio是一年前Google I/O上推出的一款Android开发IDE,他基于JetBrains’ IntelliJ IDEA,目前还在preview阶段.增强了布局拖拽和预览功 ...
- 大数据笔记11:MapReduce的运行流程
1.基本概念 (1)Job & Task (2)JobTracker (3)TaskTracker
- js 全国城市3级联动
js /* * 全国三级城市联动 js版 */ function Dsy(){ this.Items = {}; } Dsy.prototype.add = function(id,iArray){ ...