效果图
[运行环境]
Windows系统
[问题描述]
设计一个校园导游程序,为来访的客人提供各种信息查询服务。
[基本要求]
(1)设计你的学校的校园平面图,所含景点不少于10个。以图中顶点表示校内各景点,存放景点名称、代号、简介等信息;以边表示路径,存放路径长度等相关信息。
(2)为来访客人提供图中任意景点相关信息的查询。
(3)为来访客人提供图中任意景点的问路查询,即查询任意两个景点之间的一条最短的简单路径。
[测试数据]
由读者根据实际情况指定。
[实现提示]
一般情况下,校园的道路是双向通行的,可设校园平面图是一个无向网。顶点和边均含有相关信息。
[选做内容]
(1)求校园图的关节点。
(2)提供图中任意景点问路查询,即求任意两个景点之间的所有路径。(已做)
(3)提供校园图中多个景点的最佳访问路线查询,即求途经这多个景点的最佳(短)路径。
(4)校园导游图的景点和道路的修改扩充功能。(已做)
(5)扩充道路信息,如道路类别(车道、人行道等)、沿途景色等级,以至可按客人所需分别查询人行路径或车行路径或观景路径等。
(6)扩充每个景点的邻接景点的方向等信息,使得路径查询结果能提供详尽的导向信息。
(7)实现校园导游图的仿真界面。
1. 需求分析
设计一个校园导游程序,为来访的客人提供各种信息查询服务。根据学校平面图设计一个校园导游咨询系统,系统中存有景点。可实现功能:
(1)设计你的学校的校园平面图,所含景点不少于10个。以图中顶点表示校内各景点,存放景点名称、代号、简介等信息;以边表示路径,存放路径长度等相关信息。
(2)为来访客人提供图中任意景点相关信息的查询。
(3)为来访客人提供图中任意景点的问路查询,即查询任意两个景点之间的一条最短的简单路径。
(4)提供图中任意景点问路查询,即求任意两个景点之间的所有路径。
(5)校园导游图的景点和道路的修改扩充功能。
数据的条件限定:采用无向图-邻接数组的存储结构,点的类型是自定义结构体spot类型,其中含int和字符串类型,邻接数组类型为int**类型。
操作的限定:根据选择提示进行选择,限定输入所规定范围内的int类型作选择操作,有时要输入字符串如修改景点的信息。
输出的限定:输出的是字符串或十进制数。
2. 概要设计
(1) ADT的定义
无向图-邻接数组的存储结构,邻接数组里的顶点数组是spot类型,关系数组是int**类型,其中存有权值,还存有定点数和边数,且有tags[]标志数组,可用于在图中的遍历中标记顶点访问与否,spot类型是自定义的用于存储景点信息的结构体,其中包括有景点位序、名称和简介。DistInfo类型是用于迪杰斯特拉算法,其中当前最短路径上该顶点的前驱顶点的位序和当前最短路径的长度。
(2)程序模块的划分
1)主程序模块
先创建邻接数组类型无向图,再提供用户界面供用户选择功能,用switch语句供用户选择
功能模块:
2)LocateVex()
定位点v在G中的位序
3)InitGraph()
初始化无向图G,初始话vexs数组等等
4)CreateGraph()
创建无向图G,先调用初始化函数再根据已录入的数据创建
5)修改信息类
修改信息的功能只需直接在所创建的MGraph类型的图G中修改其spot的信息或arcs邻接数组的信息即可
6)FirstAdjVex()
求图G中k顶点的第一个邻接顶点的位序
7)NextAdjVex()
m顶点为k顶点的邻接顶点,求图中k顶点相对于m顶点的下一个邻接顶点的位序
8)visit()
访问结点信息(读取景点信息)
9)DFS_ALL()
基于DFS法找到所有路径函数,算法思想:从点u开始,每访问一个点,将其加入到path数组中,标记为VISITED,然后用循环访问其邻接顶点,如果被访问的点在此次所在的路径中之前已被访问,或该顶点没有邻接顶点了,且该顶点不是目的顶点,则返回上一个顶点并将其置为UNVISITED,length--,继续查找其他邻接点。如果该顶点是目的顶点,则将当前path中存储的顶点输出,就是一条路径了,之后将上一个顶点置为UNVISITED,length--,观察是否还有其余邻接点,如果有继续查找,没有则返回上一个顶点,置为UNVISITED后,继续用DFS法查找
10)Dijkstra()
迪杰斯特拉算法。求图G中从i顶点到其他所有顶点的最短路径,并由Dist返回
11)Outputpath()
沿Dist数组的prev域,可递归获得源点到k定点的最短路径
12)reviseSpot()
修改顶点信息
13)reviseArc()
修改边(道路)信息
14)creatVA()
录入点和边的信息
15)menu()
打印菜单
16)spotList()
打印景点列表
17)printmap()
打印简易地图
#include
#include
#include
#define UNVISITED 0
#define VISITED 1
#define INFINITY INT_MAX
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW 0
#define UNSELECTED 0
#define SELECTED 1
typedef int Status;
typedef enum{DG,DN,UDG,UDN} GraphKind; //图的四种类型:有向图,有向带权图,无向图和无向带权图
typedef struct{
int symbol;//景点代号
char *name;//景点名字
char *introduction;//景点简介
}spot; //景点类型
typedef struct{
spot v,w; //边的端点
int info; //对带权图,为权值,此处为距离
}ArcInfo; //存储边信息
typedef struct{
spot *vexs; //顶点数组,spot是景点类型
int **arcs; //关系数组,此图为带权图,则为权值或INFINITY
int n,e; //顶点数和边数
GraphKind kind;// 图的类型
int *tags; //标志数组,可用于在图的遍历中标记顶点访问与否
}MGraph; //邻接数组类型
typedef struct{
int prev; //当前最短路径上该顶点的前驱顶点的位序
int lowcost;//当前最短路径的长度
}DistInfo;//V-U中定点的当前最短路径信息
int LocateVex(MGraph G,spot v){
int i;
for(i=0;i0&&NULL==vexs)) return ERROR;
if(G.kind!=UDN) return ERROR;
info=INFINITY; //带权图
G.n = n;
G.e = 0; //顶点数和边数
if(0==G.n) return OK; //空图
if(NULL==(G.vexs=(spot*)malloc(n*sizeof(spot)))) return OVERFLOW;
for(i=0;i0&&NULL==vexs)||(e>0&&NULL==arcs)) return ERROR;
G.kind=kind;
int i,j,k;
spot v,w;
if(InitGraph(G,vexs,n)!=OK) return ERROR; //初始化
G.e = e;//边数
for(k=0;k=G.n) return -1;
for(i=0;i=G.n||m<0||m>=G.n) return ERROR;
for(i=m+1;i=G.n) return ERROR;
printf("景点序号:%d\n",G.vexs[k].symbol);
printf("景点名字:%s\n",G.vexs[k].name);
printf("景点简介:%s\n",G.vexs[k].introduction);
return OK;
}
Status DFS_ALL(MGraph G,int u,int v,int length,int *path,int &sum,int &flag){
// 基于DFS法找到所有路径函数
/* 算法思想:从点u开始,每访问一个点,将其加入到path数组中,标记为VISITED,然后用循环
访问其邻接顶点,如果被访问的点在此次所在的路径中之前已被访问,或该顶点没有邻接顶点了
,且该顶点不是目的顶点,则返回上一个顶点并将其置为UNVISITED,length--,继续查找其他邻接点。
如果该顶点是目的顶点,则将当前path中存储的顶点输出,就是一条路径了,之后将上一个顶点
置为UNVISITED,length--,观察是否还有其余邻接点,如果有继续查找,没有则返回上一个顶点,
置为UNVISITED后,继续用DFS法查找
*/
int i,j;
if(u<0||u>=G.n) return OK;
G.tags[u]=VISITED; //标记为VISITED
path[length]=u; //length可用于表示当前路径长度,初始值为0
length++;
if(u==v&&length>=1){
flag=1;
printf("路径%d: ",++sum);
for(j=0;j=0;i=NextAdjVex(G,u,i)){
if(G.tags[i]==UNVISITED)
DFS_ALL(G,i,v,length,path,sum,flag); //DFS法查找
}
}
G.tags[u]=UNVISITED; //该点查找完后,置为UNVISITED,并且path的路径长度-1
length--; //退当前点并回溯
return OK;
}
Status Dijkstra(MGraph G,int i,DistInfo* &Dist){
//求图G中从i顶点到其他所有顶点的最短路径,并由Dist返回
//迪杰斯特拉算法
int j,m,k,min,p;
Dist = (DistInfo*)malloc(G.n*sizeof(DistInfo));
for(j=0;j=0;p=NextAdjVex(G,k,p)){ //更新Dist数组
if(UNSELECTED==G.tags[p]&&Dist[k].lowcost+G.arcs[k][p]G.n){
printf("输入数据有误,请重新输入\n");
scanf("%d",&num);
}
printf("请输入修改后的景点名称\n");
scanf("%s",st);
G.vexs[num-1].name=st;
}
else { //修改简介
printf("景点号是多少?\n");
scanf("%d",&num);
while(num<=0||num>G.n){
printf("输入数据有误,请重新输入\n");
scanf("%d",&num);
}
printf("请输入修改后的景点简介\n");
scanf("%s",st);
G.vexs[num-1].introduction=st;
}
printf("修改完毕\n");
}
void reviseArc(MGraph &G){ //修改边(道路)信息
int i,j,num;
printf("1、删除道路 2、增加道路 3、修改边道路\n");
scanf("%d",&i);
while(i!=1&&i!=2&&i!=3){
printf("输入数据有误,请重新输入\n");
scanf("%d",&i);
}
if(1==i){
printf("你要删除的道路是哪条\n");
printf("例如:4,5 表示删除4和5景点之间的道路,中间用逗号隔开\n");
scanf("%d,%d",&i,&j);
while(i<=0||j<=0||i>G.n||j>G.n||i==j){
printf("输入数据有误,请重新输入\n");
printf("你要删除的道路是哪条?\n");
printf("例如:4,5 表示删除4和5景点之间的道路,中间用逗号隔开\n");
scanf("%d,%d",&i,&j);
}
if(G.arcs[i-1][j-1]==INFINITY){
printf("这两个景点原本没有相连,所以无法删除\n");
return;
}
G.arcs[i-1][j-1]=INFINITY;
G.arcs[j-1][i-1]=INFINITY;
G.e--;
}
else if(2==i){
printf("你要增加的道路是哪条\n");
printf("例如:4,5 表示增加4和5景点之间的道路,中间用逗号隔开\n");
scanf("%d,%d",&i,&j);
while(i<=0||j<=0||i>G.n||j>G.n||i==j){
printf("输入数据有误,请重新输入\n");
printf("你要增加的道路是哪条\n");
printf("例如:4,5 表示增加4和5景点之间的道路,中间用英文逗号隔开\n");
scanf("%d,%d",&i,&j);
}
if(G.arcs[i-1][j-1]!=INFINITY){
printf("这条路已有\n");
return;
}
printf("这条道路有多长?(单位:百米,输入1表示100米)\n");
scanf("%d",&num);
G.arcs[i-1][j-1]=num;
G.arcs[j-1][i-1]=num;
G.e++;
}
else{
printf("你要修改哪条道路?\n");
printf("例如:4,5 表示修改4和5景点之间的道路信息,中间用英文逗号隔开\n");
scanf("%d,%d",&i,&j);
while(i<=0||j<=0||i>G.n||j>G.n){
printf("输入数据有误,请重新输入\n");
printf("你要修改哪条道路?\n");
printf("例如:4,5 表示修改4和5景点之间的道路信息,中间用英文逗号隔开\n");
scanf("%d,%d",&i,&j);
}
if(G.arcs[i-1][j-1]==INFINITY) {
printf("这两个景点原本没有相连,所以无法修改\n");
return;
}
printf("把该道路修改为多长?(单位:百米,输入1表示100米)\n");
scanf("%d",&num);
G.arcs[i-1][j-1]=num;
G.arcs[j-1][i-1]=num;
}
printf("修改完毕\n");
}
void creatVA(spot *vexs,ArcInfo *arcs){
vexs[0].symbol=1;
vexs[0].name="正门";
vexs[0].introduction="学校的正门,雄伟壮观的大柱子竖立在此处,体现了我们学校的特色";
vexs[1].symbol=2;
vexs[1].name="行政楼";
vexs[1].introduction="学校行政机构的办公地点";
vexs[2].symbol=3;
vexs[2].name="工学楼";
vexs[2].introduction="工学楼是工科学子学习知识,提升知识水平,做实验的地方";
vexs[3].symbol=4;
vexs[3].name="图书馆";
vexs[3].introduction="呈魔方形状的图书馆座落在人工湖边,这里是知识的海洋";
vexs[4].symbol=5;
vexs[4].name="人工湖";
vexs[4].introduction="有水有绿化,此处正是人工湖,同学们休憩,观景的好地方";
vexs[5].symbol=6;
vexs[5].name="教学楼";
vexs[5].introduction="这里拥有6栋教学楼,宽敞的教室给学子们提供良好的学习环境";
vexs[6].symbol=7;
vexs[6].name="体育馆";
vexs[6].introduction="篮球场、足球场、网球场等应有尽有,这里是强身健体的地方";
vexs[7].symbol=8;
vexs[7].name="饭堂1";
vexs[7].introduction="座落在学校东边的一饭,菜式多样,品种齐全";
vexs[8].symbol=9;
vexs[8].name="东宿舍区";
vexs[8].introduction="东宿舍区距离教学区较近,学生多步行上课";
vexs[9].symbol=10;
vexs[9].name="饭堂2";
vexs[9].introduction="饭堂2,菜式多样,品种齐全";
vexs[10].symbol=11;
vexs[10].name="西宿舍区";
vexs[10].introduction="西宿舍区距离教学区较远,学生多骑自行车上课";
//边信息
arcs[0].v=vexs[0];
arcs[0].w=vexs[1];
arcs[0].info=1;
arcs[1].v=vexs[1];
arcs[1].w=vexs[2];
arcs[1].info=3;
arcs[2].v=vexs[1];
arcs[2].w=vexs[3];
arcs[2].info=4;
arcs[3].v=vexs[2];
arcs[3].w=vexs[3];
arcs[3].info=6;
arcs[4].v=vexs[3];
arcs[4].w=vexs[4];
arcs[4].info=1;
arcs[5].v=vexs[1];
arcs[5].w=vexs[5];
arcs[5].info=4;
arcs[6].v=vexs[2];
arcs[6].w=vexs[5];
arcs[6].info=4;
arcs[7].v=vexs[3];
arcs[7].w=vexs[5];
arcs[7].info=5;
arcs[8].v=vexs[3];
arcs[8].w=vexs[6];
arcs[8].info=6;
arcs[9].v=vexs[2];
arcs[9].w=vexs[7];
arcs[9].info=7;
arcs[10].v=vexs[5];
arcs[10].w=vexs[6];
arcs[10].info=9;
arcs[11].v=vexs[5];
arcs[11].w=vexs[7];
arcs[11].info=6;
arcs[12].v=vexs[5];
arcs[12].w=vexs[8];
arcs[12].info=4;
arcs[13].v=vexs[5];
arcs[13].w=vexs[9];
arcs[13].info=6;
arcs[14].v=vexs[3];
arcs[14].w=vexs[9];
arcs[14].info=7;
arcs[15].v=vexs[6];
arcs[15].w=vexs[10];
arcs[15].info=3;
arcs[16].v=vexs[7];
arcs[16].w=vexs[8];
arcs[16].info=4;
arcs[17].v=vexs[8];
arcs[17].w=vexs[9];
arcs[17].info=5;
arcs[18].v=vexs[9];
arcs[18].w=vexs[10];
arcs[18].info=5;
}
void menu(){
printf("\t\t\t ***校园导游咨询***\n");
printf("\t\t\t*******************************\n");
printf("\t\t\t* *\n");
printf("\t\t\t* 1、查看景点信息 *\n");
printf("\t\t\t* 2、查看两景点间的最短路径 *\n");
printf("\t\t\t* 3、查看两景点间的所有路径 *\n");
printf("\t\t\t* 4、查看菜单 *\n");
printf("\t\t\t* 5、查看景点列表 *\n");
printf("\t\t\t* 6、查看简易地图 *\n");
printf("\t\t\t* 7、修改景点信息 *\n");
printf("\t\t\t* 8、修改或扩充道路 *\n");
printf("\t\t\t* 0、退出系统 *\n");
printf("\t\t\t* *\n");
printf("\t\t\t*******************************\n\n");
}
void spotList(){
printf("\t\t\t***********景点列表************ \n");
printf("\t\t ------------------------------------------\n");
printf("\t\t 1.正门 2.行政楼 3.工学楼 4.图书馆\n");
printf("\t\t 5.人工湖 6.教学楼 7.体育馆 8.饭堂1 \n");
printf("\t\t 9.东宿舍区 10.饭堂2 11.西宿舍区 \n");
printf("\t\t ------------------------------------------\n");
}
void printmap(){
printf("\t *********简易地图(仅限修改前)********* \n");
printf("\t11-----------10--------------9--------------8 \n");
printf("\t| | \\ | / | \n");
printf("\t| | \\ | / | \n");
printf("\t| | \\ | / | \n");
printf("\t| | \\ | / | \n");
printf("\t| | \\ | / | \n");
printf("\t| | \\ | / | \n");
printf("\t7-------------|--------------6 | \n");
printf("\t \\ | / | \\ | \n");
printf("\t \\ | / | \\ | \n");
printf("\t \\ | / | \\ | \n");
printf("\t \\ | / | \\ | \n");
printf("\t \\ | / | \\ | \n");
printf("\t \\ | / | \\ | \n");
printf("\t 4--------------|--------------3 \n");
printf("\t / \\ | / \n");
printf("\t / \\ | / \n");
printf("\t / \\ | / \n");
printf("\t 5 2 \n");
printf("\t | \n");
printf("\t | \n");
printf("\t 1 \n");
}
int main(){
int n=11,e=19,sum=0,flag=0;
spot *vexs;
ArcInfo *arcs;
int *path; //用于存储路径
int length; //当前路径长度
vexs=(spot*)malloc(n*sizeof(spot)); //分配空间
arcs=(ArcInfo*)malloc(e*sizeof(ArcInfo));
path=(int*)malloc(e*sizeof(int));
length=0;
MGraph G;
DistInfo *Dist; //定义数组
creatVA(vexs,arcs);
CreateGraph(G,UDN,vexs,n,arcs,e);
int choice=INFINITY,i,j;
printf("\n");
menu();
printf("\n");
spotList();
while(choice){
printf("\n ---------------\n");
printf(" | 请选择功能 |\n");
printf(" ---------------\n");
scanf("%d",&choice);
switch(choice)
{
case 1:
printf("你想查看哪个景点的信息(1-%d)?\n",G.n);
scanf("%d",&i);
while(i<=0||i>G.n){
printf("你输入的数据不合法,请重新输入\n");
scanf("%d",&i);
}
visit(i-1,G);
break;
case 2:
for(i=0;iG.n){
printf("你输入的数据不合法,请重新输入\n");
scanf("%d",&i);
}
printf("输入第二个点的序号(1-%d)\n",G.n);
scanf("%d",&j);
while(j<=0||j>G.n){
printf("你输入的数据不合法,请重新输入\n");
scanf("%d",&j);
}
Dijkstra(G,i-1,Dist); //迪杰斯特拉算法求出Dist数组
if(Dist[j-1].lowcost==INFINITY){
printf("没有连通\n");
break;
}else{
printf("路线:");
Outputpath(G,Dist,j-1); //递归输出
printf("\n");
printf("路径长度为:%d米\n",Dist[j-1].lowcost*100);
break;
}
case 3:
for(i=0;iG.n){
printf("你输入的数据不合法,请重新输入\n");
scanf("%d",&i);
}
printf("输入第二个点的序号(1-%d)\n",G.n);
scanf("%d",&j);
while(j<=0||j>G.n){
printf("你输入的数据不合法,请重新输入\n");
scanf("%d",&j);
}
flag=0;
DFS_ALL(G,i-1,j-1,length,path,sum,flag);
if(flag==0){printf("没有连通\n");}
break;
case 4:
menu();break;
case 5:
spotList();break;
case 6:
printmap();break;
case 7:
reviseSpot(G);break;
case 8:
reviseArc(G);break;
case 0:
printf("退出系统,欢迎你下次再来\n");
break;
default:
printf("输入数据不合法,请重新选择\n");break;
}
}
return 0;
}