一、 目的与要求
1、目的:
通过布置具有一定难度的实际程序设计项目,使学生进一步理解和掌握课堂上所学各种基本抽象数据类型的逻辑结构、存储结构和操作实现算法,以及它们在程序中的使用方法;使学生掌握分析问题,求解问题的方法并提高学生设计编程实现的能力。
2、要求: 基本要求:
1.要求利用C\C++语言来完成系统的设计;
2.突出C语言的函数特征(以多个函数实现每一个子功能)或者C++语言面向对象的编程思想;
3.画出功能模块图;
4.进行简单界面设计,能够实现友好的交互;
5.具有清晰的程序流程图和数据结构的详细定义;
6.熟练掌握C语言或者C++语言的各种操作。创新要求: 在基本要求达到后,可进行创新设计,如系统用户功能控制,改进算法的实现,实现友好的人机交互等等
二、 问题描述和求解方法
1 、问题描述(功能要求):
可以任意定义一个迷宫,用非递归的方法求出走出迷宫的通路,并把路径输出出来。
要求: 存储结构、基本算法(可以使用程序流程图)、源程序、测试数据和结果、算法的 时间复杂度、另外可以提出算法的改进方法。
1) 迷宫的存储结构要合理;
2) 应该考虑算法的时间和空间复杂度。
3) 当确定迷宫的规模以及形态以后要把至少一条能走出迷宫的路径输出出来;
4)程序应当满足正确性、可读性、健壮性和高效率及低存储量等目标要求,遵循代码规 范,方便调试和阅读。
2 、问题的解决方案:
根据系统功能要求,可以将问题解决分为以下步骤:
( 1 )迷宫可以采用二维数组来存储,迷宫的通路状态可以用不同的字符来表示;
( 2 )根据问题描述,设计算法的实现;
( 3 )建议在解决问题时要采用栈或者队列数据结构;
( 4 )完成算法的各个功能模块;
( 5 )功能调试;
( 6 )完成系统总结报告以及系统使用说明书。
三、 解题过程
1.分析程序的功能要求,划分程序功能模块。
2.画出系统流程图。
3.代码的编写。定义数据结构和各个功能子函数。
4.程序的功能调试。
5.完成系统总结报告以及使用说明书
四、 实现源码
#include<iostream>
#include<stack>
#include<queue>
#include<cstring>
using namespace std;
const int MAXN = 1005;
int n,m,b,c,starx,stary,endx,endy,a[MAXN][MAXN],book[MAXN][MAXN],ans,flag1,flagsee;
int minn = 999999,num,flagY=1;
struct node{
int x,y;
int s; //x,y为坐标,s为步数
int fx,fy;//fx,fy代表该点前面的点的坐标
};
node bfsmin[MAXN];
node minfinal[MAXN];
stack<node> st;
stack<node> minst;
queue<node> que;
node MAP,head,tail;
node minMAP[MAXN];
node otherMAP[MAXN];
int flag=0,minflag=1;
void outmap(stack<node> st)//输出可行方案数
{
printf("可行方案%d:",++flag);
int ans1 = 0;
while(!st.empty())
{
otherMAP[ans1++] = st.top();
st.pop();
}
//printf("(%d,%d)",starx,stary);
cout<<"起始点";
for(int i = ans-1;i>0;i--)
{
printf("->(%d,%d)",otherMAP[i].x,otherMAP[i].y);
if((ans-i) != 0 && (ans-i) % 10 == 0)
printf("\n");
}
cout<<"->终止点";
cout<<endl;
}
void outmin(node bfsmin[MAXN],int minflag)
{
int find = minflag;
int tx = bfsmin[find].fx;
int ty = bfsmin[find].fy;
int x=0,y=0;
while(bfsmin[find].fx != -1 || bfsmin[find].fy!=-1)
{
int y = 0 ;
for(int i=1;i<=minflag;i++)
{
if(bfsmin[i].x == tx&& bfsmin[i].y == ty)
{
minfinal[x++] = bfsmin[i];
find = i;
tx = bfsmin[i].fx;ty = bfsmin[i].fy;
if(x%10 == 0)
{
y = 1;
break;
}
}
}
if(y==1)
break;
}
cout<<"☆最短路径长度为:"<<x<<endl;
cout<<"☆最短路径为:"<<endl;
for(int i=x-1;i>=0;i--)
{
printf("(%d,%d)->",minfinal[i].x,minfinal[i].y);
}
printf("(%d,%d)",endx,endy);
}
void outseeing(stack<node> st)//所有可行方案的可视图
{
while(!st.empty())
{
a[st.top().x][st.top().y] = 2;
st.pop();
}
printf("可行方案%d:\n",++flagsee);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i][j] == 2)
{
if(i == endx && j == endy)
cout<<"★";
else
cout<<"□";
a[i][j] = 0;
}
else if(a[i][j] == 0)
{
if(i == starx && j == stary)
cout<<"◆";
else
cout<<"█";
}
else if(a[i][j] == 1)
cout<<"▓";
}
cout<<endl;
}
cout<<endl;
}
void outminseeing(node bfsmin[MAXN],int minflag)//最短路径的可视图
{
int x = 0;
int find = minflag;
int tx = bfsmin[find].fx;
int ty = bfsmin[find].fy;
while(bfsmin[find].fx != -1 || bfsmin[find].fy!=-1)
{
int y = 0 ;
for(int i=1;i<=minflag;i++)
{
if(bfsmin[i].x == tx&& bfsmin[i].y == ty)
{
minfinal[x++] = bfsmin[i];
find = i;
tx = bfsmin[i].fx;ty = bfsmin[i].fy;
if(x%10 == 0)
{
y = 1;
break;
}
}
}
if(y==1)
break;
}
cout<<"☆最短路径长度为:"<<x<<endl; ;
cout<<"☆最短路径为:"<<endl;
for(int i = x-1;i>=0;i--)
a[minfinal[i].x][minfinal[i].y] = 2;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i][j] == 2)
{
if(i==starx && j==stary)
cout<<"★";
else
cout<<"□";
a[i][j] = 0;
}
else if(a[i][j] == 0)
{
if(i == endx && j == endy)
cout<<"◆";
else
cout<<"█";
}
else if(a[i][j] == 1)
cout<<"▓";
}
cout<<endl;
}
}
void dfs(int midx,int midy,int step)//深度优先搜索 (dfs)
{
int nx,ny;
if(midx == endx && midy == endy)
{
if(minn > step)
{
minn = step;
flagY = 1;
//flag1 = copyMAP(st);
}
if(num == 2) outmap(st);
if(num == 4) outseeing(st);
return ;
}
int next[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
for(int i=0;i<4;i++)
{
nx = midx+next[i][0];
ny = midy+next[i][1];
if(nx>n||nx<1||ny>m||ny<1)
continue;
if(a[nx][ny]==0&&book[nx][ny]==0)
{
MAP.x = nx; MAP.y = ny;
ans++;
st.push(MAP);
//cout<<ans<<" ";
book[nx][ny]=1;
dfs(nx,ny,step+1);
book[nx][ny]=0;
st.pop();
ans--;
}
}
return ;
}
void bfs()//广度优先搜索
{
int next[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int bfsflag = 0,tx,ty;
head.x = tail.x = starx;
head.y = tail.y = stary;
head.fx = tail.fx = -1;
head.fy = tail.fy = -1;
head.s = tail.s = 0;
que.push(head);
bfsmin[minflag++] = que.front();
while(!que.empty())
{
for(int i=0;i<4;i++)
{
tx = que.front().x + next[i][0];
ty = que.front().y + next[i][1];
if(tx>n||tx<1||ty>m||ty<1)
continue;
if(a[tx][ty] == 0 && book[tx][ty] == 0)
{
book[tx][ty] = 1;
tail.x = tx; tail.y = ty;
tail.fx = que.front().x;
tail.fy = que.front().y;
tail.s = que.front().s + 1;
que.push(tail);
}
if(tx == endx && ty == endy)
{
bfsflag = 1;
bfsmin[minflag++] = tail;
if(num == 3)
outmin(bfsmin,minflag-1);
else if(num == 5)
outminseeing(bfsmin,minflag-1);
break;
}
}
que.pop();
bfsmin[minflag++] = que.front();
if(bfsflag == 1)
break;
}
}
int main()
{
cout<<"\n";
cout<<"\t\t 欢迎使用迷宫程序"<<endl;
cout<<"☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆"<<endl;
cout<<"☆☆\t\t\t1.输入迷宫地图\t\t\t☆☆"<<endl;
cout<<"☆☆\t\t2.寻找两个点在迷宫的所有可行方案\t☆☆"<<endl;
cout<<"☆☆\t\t 3.寻找两点在迷宫中的最短路径\t\t☆☆"<<endl;
cout<<"☆☆\t 4.寻找两个点在迷宫的所有可行方案的可视图 ☆☆"<<endl;
cout<<"☆☆\t\t5.寻找两点在迷宫中的最短路径的可视图 \t☆☆"<<endl;
cout<<"☆☆\t\t\t6.结束操作\t\t\t☆☆"<<endl;
cout<<"☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆"<<endl;
cout<<"☆输入对应数字实现相应功能:"<<endl;
while(cin>>num)
{
int flagnum = 0;
switch(num)
{
case(1):
{
cout<<"☆输入迷宫的长和宽:"<<endl;
cin>>n>>m;
cout<<"☆输入迷宫地图:";
cout<<"(其中0代表可走;1代表为障碍物,不可走)"<<endl;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
}
cout<<"☆输入成功,输入其他数字执行替他操作"<<endl;
break;
}
case(2):
{
memset(book,0,sizeof(book));
cout<<"☆输入起点坐标:"<<endl;
cin>>starx>>stary;
book[starx][stary]=1;
cout<<"☆输入终点坐标:"<<endl;
cin>>endx>>endy;
dfs(starx,stary,0);
if(a[starx][stary]==1 || a[endx][endy]==1)
flagY=0;
if(flagY == 0)
cout<<"没有可行路径,或者坐标超出迷宫范围!!"<<endl;
cout<<"☆操作成功,输入其他数字执行替他操作"<<endl;
break;
}
case(3):
{
memset(book,0,sizeof(book));
cout<<"☆输入起点坐标:"<<endl;
cin>>starx>>stary;
book[starx][stary]=1;
cout<<"☆输入终点坐标:"<<endl;
cin>>endx>>endy;
if(a[starx][stary]==1 || a[endx][endy]==1)
flagY=0;
if(flagY == 0)
cout<<"没有可行路径,或者坐标超出迷宫范围!!"<<endl;
else if(starx == endx && stary == endy)
cout<<"☆最短路径长度为:0"<<endl;
else
{
bfs();
cout<<endl;
cout<<"☆操作成功,输入其他数字执行替他操作"<<endl;
}
break;
}
case(4):
{
memset(book,0,sizeof(book));
cout<<"☆输入起点坐标:"<<endl;
cin>>starx>>stary;
book[starx][stary]=1;
cout<<"☆输入终点坐标:"<<endl;
cin>>endx>>endy;
cout<<"◆代表起始点\t★代表终止点"<<endl;
cout<<"□代表该方案中要走的位置"<<endl;
cout<<"█代表迷宫中可走的位置,但该方案中未走的位置"<<endl;
cout<<"▓代表迷宫中不可走的位置"<<endl;
cout<<endl;
dfs(starx,stary,0);
if(a[starx][stary]==1 || a[endx][endy]==1)
flagY=0;
if(flagY == 0)
cout<<"没有可行路径,或者坐标超出迷宫范围!!"<<endl;
else
cout<<"☆操作成功,输入其他数字执行替他操作"<<endl;
break;
}
case(5):
{
memset(book,0,sizeof(book));
cout<<"☆输入起点坐标:"<<endl;
cin>>starx>>stary;
book[starx][stary]=1;
cout<<"☆输入终点坐标:"<<endl;
cin>>endx>>endy;
cout<<"◆代表起始点\t★代表终止点"<<endl;
cout<<"□代表该方案中要走的位置"<<endl;
cout<<"█代表迷宫中可走的位置,但该方案中未走的位置"<<endl;
cout<<"▓代表迷宫中不可走的位置"<<endl;
cout<<endl;
dfs(starx,stary,0);
if(a[starx][stary]==1 || a[endx][endy]==1)
flagY=0;
if(flagY == 0)
cout<<"没有可行路径,或者坐标超出迷宫范围!!"<<endl;
else if(starx == endx && stary == endy)
cout<<"☆最短路径长度为:0"<<endl;
else
{
bfs();
cout<<"☆操作成功,输入其他数字执行替他操作"<<endl;
}
break;
}
case(6):
{
flagnum = 1;
cout<<"\t☆操作结束☆"<<endl;
break;
}
default:
{
cout<<"☆未找到对应的功能,请正确输入与相关操作对应的数字"<<endl;
break;
}
}
if(flagnum == 1)
break;
}
return 0;
}
/*
5 4
1 1
4 3
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
*/
五、 相关案例
案例一
#include <iostream>
#include <string.h>
#include <stack>
#include <queue>
#include <algorithm>
using namespace std;
int dx[4]={0,-1,1,0};//方向
int dy[4]={-1,0,0,1};
bool vis[6][6];
int total=0;//多少可到达路径
int sx=1,sy=1;//入口出口坐标
int ex=4,ey=4;
int num[10][10];//广搜时记录到达当前点的最少步数
struct P
{
int x,y;
}point[40];//用来记录可到达路径
struct PP
{
int fx,fy;
}path[10][10];//用来记录最短路径坐标增量,用于回溯输出最短路径
char map[6][6]=//地图
{
{'#','#','#','#','#','#'},
{'#','.','.','.','#','#'},
{'#','.','#','.','.','#'},
{'#','.','.','.','#','#'},
{'#','#','.','.','.','#'},
{'#','#','#','#','#','#'}
};
bool ok(int x,int y)//判断当前点是否可走
{
if(x<0||x>5||y<0||y>5)
return 0;
if(map[x][y]=='#')
return 0;
if(vis[x][y]==1)
return 0;
return 1;
}
void dfs(int x,int y,int step)//深搜可到达路径,参数step对于记录路径来说很重要
{
if(x==ex&&y==ey)
{
total++;
cout<<"第"<<total<<"条路径为: ";
for(int i=0;i<step;i++)
cout<<"("<<point[i].x<<","<<point[i].y<<")";
cout<<endl;
return;
}
for(int i=0;i<4;i++)
{
int curx=x+dx[i];
int cury=y+dy[i];
if(ok(curx,cury))
{
vis[curx][cury]=1;
point[step].x=curx;point[step].y=cury;//记录路径
dfs(curx,cury,step+1);
vis[curx][cury]=0;
}
}
}
void bfs(int x,int y)//广搜求最短路径
{
num[x][y]=0;
queue<P>q;
P a,b;
a.x=x;
a.y=y;
path[a.x][a.y].fx=0;path[a.x][a.y].fy=0;
q.push(a);
while(!q.empty())
{
b=q.front();
q.pop();
for(int i=0;i<4;i++)
{
a.x=b.x+dx[i];
a.y=b.y+dy[i];
if(ok(a.x,a.y))
{
vis[a.x][a.y]=1;
q.push(a);
path[a.x][a.y].fx=dx[i];
path[a.x][a.y].fy=dy[i];
num[a.x][a.y]=num[b.x][b.y]+1;//记录步数
}
}
}
}
void print(int x,int y)//输出最短路径
{
if(x==sx&&y==sy)
{
cout<<"(1,1)";
return;
}
print(x-path[x][y].fx,y-path[x][y].fy);
cout<<"("<<x<<","<<y<<")";
}
int main()
{
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
vis[sx][sy]=1;
point[0].x=sx;point[0].y=sy;
dfs(sx,sy,1);
cout<<"总计有"<<total<<"条可到达路径"<<endl;
memset(vis,0,sizeof(vis));
vis[sx][sy]=1;
bfs(sx,sy);
cout<<"到达各个点的最少步数如下:"<<endl<<endl;
for(int i=0;i<6;i++)
{
for(int j=0;j<6;j++)
cout<<num[i][j]<<" ";
cout<<endl;
}
cout<<endl;
cout<<"最少要走"<<num[ex][ey]<<"步才能走到出口"<<endl<<endl;;
cout<<"其中一条最短路径为:";print(ex,ey);cout<<endl;
return 0;
}
案例二
#include<iostream>
using namespace std;
void EnQueue(int i,int j,int k); //入队一个节点
void DeQueue(int *i,int *j,int *k); //获取当前节点的序号和对应的迷宫坐标,然后出列
bool GetNextPos(int *i ,int *j,int count); //得到下一个邻接点的位置
void ShortestPath_BFS(int i,int j); //广度优先遍历寻找最短路径
void ShortestPath(); //输出最短路径
void Print(); //输出迷宫形状
int Map[10][10] = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},{1,1,0,0,0,0,0,0,0,0},{1,1,1,1,1,1,1,1,1,1}
};
struct Node {
int parent_id; //保存父节点的位置
int node_id; //当前节点的序号,以便传递给孩子节点
int x,y; //当前结点对应的坐标
} Q[10*10]; //每个节点包含迷宫坐标、队列中的序号、父节点的序号,多个节点形成队列
int front = 0,rear = 0; //队列头指针和尾指针
int main() {
cout<<"程序说明:"<<'\n'<<"1.输出路径为最短路径;"<<'\n'<<"2.默认的出口在最右下角,如有需要可以调整。"<<'\n'<<'\n';
cout<<"初始地图如下:"<<endl;
Print();
int i,j;
reinput:
cout<<"请输入起点坐标(x,y): "<<endl;
cin>>i>>j;
if(Map[i][j]) {
cout<<"不能从该处出发,请重新输入!"<<endl;
goto reinput;
}
ShortestPath_BFS(i,j);
cout<<"最短路径之一如下:"<<endl;
ShortestPath();
}
void EnQueue(int i,int j,int k) { //入队一个节点
Q[rear].x = i;
Q[rear].y = j; //保存当前节点对应的坐标位置
Q[rear].parent_id = k; //保存父节点的序号 ************-1
Q[rear].node_id = rear; //保存当前节点序号
rear++;
}
void DeQueue(int *i,int *j,int *k) { //获取当前节点的序号和对应的迷宫坐标,然后出列
*i = Q[front].x;
*j = Q[front].y;
*k = Q[front].node_id;
front++; //出列一个节点
}
bool GetNextPos(int *i ,int *j,int count) { //得到下一个邻接点的位置
switch(count) {
case 1:
(*j)++;
return 1; //右
case 2:
(*i)++;
return 1; //下
case 3:
(*j)--;
return 1; //左
case 4:
(*i)--;
return 1; //上
default:
return 0;
}
}
void ShortestPath_BFS(int i ,int j) { //广度优先遍历寻找最短路径
int count,m,n,k;
EnQueue(i,j,-1);
Map[i][j] = 1; //起点入队,标记起点已走过
while(true) {
count = 1;
DeQueue(&i,&j,&k);
n = i,m = j;
//保存当前位置
while(GetNextPos(&i,&j,count)) {
count++;
if(!Map[i][j]) {
EnQueue(i,j,k);
Map[i][j] = 1;
if(i == 8 && j == 9)
return; //到达终点(8,9)是默认终点,可以任意修改
}
i = n;
j = m; //保证遍历当前坐标的所有相邻位置
}
}
}
void ShortestPath() {
int i,j,k,sum=0;
k = rear-1;
while(k != -1) {
i = Q[k].x;
j = Q[k].y;
Map[i][j] = 2;
k = Q[k].parent_id;
}
cout<<" 0 1 2 3 4 5 6 7 8 9"<<endl;
for(i = 0; i < 10; i++) {
cout<<i;
for(j = 0; j < 10; j++) {
if(Map[i][j]==2) {
sum++;
cout<<"□";
} else
cout<<"■";
}
cout<<endl;
}
cout<<"最短路径长度:"<<sum<<endl;
}
void Print() {
cout<<" 0 1 2 3 4 5 6 7 8 9"<<endl;
for(int i = 0; i < 10; i++) {
cout<<i;
for(int j = 0; j < 10; j++) {
if(Map[i][j])
cout<<"■";
else
cout<<"□";
}
cout<<endl;
}
}