数据结构
实验 避暑山庄旅游路径规划项目
一、避暑山庄旅游路径规划项目的问题分析
1. 旅游路径规划过程中经常遇到问题有哪些,具体到避暑山庄旅游路径规划中可能会有哪些问题,确认问题阶段;
(1).确定起点或终点的最短路径问题:即已知起点或终点,按照题目要求,求最短路径的问题。
(2).确定起点和终点的最短路径问题:即已知起点和终点,按照题目要求,求两点之间的最短路径。
(3).全局最短路径问题:按照题目要求,求图中所有的最短路径。
2.分解问题,写出问题陈述,即把问题分解为各个比较小的问题,区分出紧急、严重性或可能性等问题
(1).主页
(2).查看路线
(3).查找最短路径
(4).查找所有路径
(5).景点介绍
(6).景区地图
(7).退出
3. 分析选择关键活动,按照问题优先度排序,即制订研究这些问题的先后程序。
4. 分析此类问题所需数据的类型、特点、关系等。
地图数据常常可以用图(Graph)这类数据结构表示,那么在图结构中常用的搜索算法也可以应用到路径规划中
二、避暑山庄旅游路径规划项目的结构设计
1.根据问题分析设计一款合适的数据结构,如何抽象和存储地图?
用printf函数根据各个景点的大概位置存储、输出地图
2.选择合适存储结构方案,写出具体的结构体定义
Typedefstruct side{ //边的权值
int wet; //权值
}side,wetmatrix[MaxNumber][MaxNumber]; //边的邻接矩阵类型
typedef struct vetinf { //顶点信息
int number; //顶点编号
char name[64]; //顶点名称
char intro[256]; //顶点介绍
}vetinf;
typedef struct mapstr { //图结构信息
vetinf vets[MaxNumber]; //顶点数组
wetmatrix mat; //邻接矩阵
int vetnum, sidenum;
}mapstr;
3.设计解决问题的基本功能/操作
1、图的初始化,由mapstr initmap()函数实现
2、查询景点在图中的序号,由int locatevet()函数实现
3、查询输入序号l,n间的长度不超过10个景点的路径,由void path()函数实现
4、查询两景点的所有路径,由int allpath()函数实现
5、迪杰斯特拉算法求单源最短路径,由void shortestpath()函数实现
6、主页,由void menu()函数实现
7、输出图的邻接矩阵的值,由void void printmapstr()函数实现
8、弗洛伊德算法,由void floyd()函数实现
9、输出数组,由void printarray()函数实现
10、输出最短路径,由void display()函数实现
11、任意两点间距离,由int shortdistance()函数实现
12、显示所有景点信息,由void compusinfor()函数实现
13、地图,由void schoolmap()函数实现
14、用户界面,由void mainwork()函数实现
三、代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Infinity 65535 //表示无穷大
#define MaxNumber 11 //用于邻接矩阵
#define vertex 9 //顶点个数
typedef struct side { //边的权值
int wet; //权值
}side, wetmatrix[MaxNumber][MaxNumber]; //边的邻接矩阵类型
typedef struct vetinf { //顶点信息
int number; //顶点编号
char name[64]; //顶点名称
char intro[256]; //顶点介绍
}vetinf;
typedef struct mapstr { //图结构信息
vetinf vets[MaxNumber]; //顶点数组
wetmatrix mat; //邻接矩阵
int vetnum, sidenum;
}mapstr;
//全局变量
mapstr campus; //图结构变量(避暑山庄总览)
int d[30];
int visited[50];
int shortest[MaxNumber][MaxNumber];//定义全局变量存储最小路径
int pathh[MaxNumber][MaxNumber];//定义存储路径
//1.图的初始化
mapstr initmap() {
mapstr m;//构件图m
int i = 0, j = 0;
m.vetnum = 9; //定义顶点个数
m.sidenum = 11; //定义边的条数
for (i = 1; i <= vertex; i++) //依次设置顶点信息
m.vets[i].number = i;
//输入顶点信息
strcpy(m.vets[1].name, "松鹤斋");
strcpy(m.vets[1].intro, "康熙时,皇太后来避暑山庄,居住在西峪的松鹤清樾。");
strcpy(m.vets[2].name, "正宫");
strcpy(m.vets[2].intro, "是宫殿区的主体建筑");
strcpy(m.vets[3].name, "东宫");
strcpy(m.vets[3].intro, "在松鹤斋的东面,地势比正宫和松鹤斋低。");
strcpy(m.vets[4].name, "万壑松风殿");
strcpy(m.vets[4].intro, "万壑松风的主殿。");
strcpy(m.vets[5].name, "烟波致爽殿");
strcpy(m.vets[5].intro, "是正宫后寝部分的主殿,也是清帝在山庄的寝宫");
strcpy(m.vets[6].name, "外八庙");
strcpy(m.vets[6].intro, "在避暑山庄的东面和北面,武烈河两岸和狮子沟北沿的山丘地带,共有11座寺院");
strcpy(m.vets[7].name, "布达拉·行宫");
strcpy(m.vets[7].intro, "坐落于避暑山庄正北狮子岭南麓,占地25.79万平方米");
strcpy(m.vets[8].name, "普宁寺");
strcpy(m.vets[8].intro, "坐落于避暑山庄东北部武烈河畔,占地5.78万平方米");
strcpy(m.vets[9].name, "磬锤峰");
strcpy(m.vets[9].intro, "坐落于避暑山庄东部,武烈河东岸,占地4005万平方米");
for (i = 1; i <= vertex; i++)
for (j = 1; j <= vertex; j++)
m.mat[i][j].wet = Infinity; //初始化图的邻接矩阵
m.mat[1][2].wet = 200;
m.mat[1][3].wet = 150;
m.mat[2][4].wet = 130;
m.mat[3][6].wet = 200;
m.mat[3][5].wet = 150;
m.mat[4][6].wet = 100;
m.mat[4][7].wet = 120;
m.mat[5][8].wet = 100;
m.mat[6][9].wet = 80;
m.mat[7][9].wet = 120;
m.mat[8][9].wet = 90;
for (i = 1; i <= vertex; i++) //无向带权图是对称矩阵,给其另一半赋值
for (j = 1; j <= vertex; j++)
m.mat[j][i].wet = m.mat[i][j].wet;
return m;
}
//2.查询景点在图中的序号
int locatevet(mapstr m, int v) {
int i;
for (i = 0; i <= m.vetnum; i++)
if (v == m.vets[i].number)
return i;//找到返回顶点i
return -1;//未找到
}
//3.查询输入序号l,n间的长度不超过10个景点的路径
void path(mapstr m, int l, int n, int k) {
int s, t = k + 1;
int length = 0; //t用于存储路径上下一顶点对应的d[]数组元素的下标
if (d[k] == n && k < 8) { //若d[k]是终点且景点个数<8,则输出该路径
for (s = 0; s < k; s++) {
length = length + m.mat[d[s]][d[s + 1]].wet;
}
if (length < 200) { //打印路径小于200(定长)的路径
for (s = 0; s < k; s++) { //输出该路径,s=0时为起点m
printf("%d%s--->", d[s], m.vets[d[s]].name);
}
printf("%d%s ", d[s], m.vets[d[s]].name); //输出最后一个顶点
printf("总路线长为%d米\n\n", length);
}
} else {
s = 1;
while (s <= m.vetnum) { //从第m个顶点,访问所有顶点是否有路径
if ((m.mat[d[k]][s].wet < Infinity) && (visited[s] == 0)) { //顶点有边且未被访问
visited[s] = 1;
d[k + 1] = s; //存储顶点编号
path(m, l, n, t);
visited[s] = 0; //将找到的路径上的顶点的访问标志重新设置为,便于探究新的路径
}
s++;//试验下一顶点s开始是否有到终点的路径;
}
}
}
//4.查询两景点的所有路径
int allpath(mapstr m) {
int k, i, j, l, n;
printf("\n\n请输入您想要查询的两个景点的编号:");
scanf("%d %d", &i, &j);
printf("\n\n");
l = locatevet(m, i); //locatevet 确定该顶点是否存在。若存在,返回该顶点编号。
n = locatevet(m, j);
d[0] = l; //路径起点l(字母).(d[]数组为全局变量)
for (k = 0; k < vertex; k++)
visited[k] = 0;
visited[l] = 1;
path(m, l, n, 0);
return 1;
}
//5.迪杰斯特拉算法求单源最短路径
void shortestpath(mapstr m) {
int v0, v, w, k = 1, min, t, p;
int final[MaxNumber];//final[w]=1表示已经求得顶点V0到Vw的的最短路径
int Pathside[MaxNumber];//用于存储最短路径下标的数组
int ShortPathwet[MaxNumber];//用于存储到各点最短路径的权值和
printf("\n请输入起始景点的编号:");
scanf("%d", &v0);
printf("\n\n");
while (v0 < 0 || v0 > vertex) { //判断是否输入正确
printf("\n您输入的景点编号不存在\n");
printf("请重新输入:");
scanf("%d", &v0);
}
for (v = 1; v <= m.vetnum; v++) { //数组初始化
final[v] = 0; //全部顶点初始化为未找到路径
ShortPathwet[v] = m.mat[v0][v].wet; //将与v0有连线的路径加上权值
Pathside[v] = 0; //初始化路径数组为0
}
ShortPathwet[v0] = 0;
final[v0] = 1;
//Dijkstr算法主体
for (v = 1; v <= m.vetnum; v++) {
min = Infinity;
for (w = 1; w <= m.vetnum; w++) { //找出离当前指向顶点最近的点
if (!final[w] && ShortPathwet[w] < min) { //未被访问且存在边
k = w;
min = ShortPathwet[w];
}
}
final[k] = 1; //将找到的离当前顶点最近的置1
//修正
for (w = 1; w <= m.vetnum; w++) {
if (!final[w] && (min + m.mat[k][w].wet < ShortPathwet[w])) {
ShortPathwet[w] = min + m.mat[k][w].wet; //修改当前最优路径长度
Pathside[w] = k; //存放前驱结点
}
}
}
printf("打印P数组:"); //打印p数组
for (t = 1; t <= m.vetnum; t++) {
printf("%d ", Pathside[t]);
}
printf("\n\n");
printf("打印S数组:"); //打印s数组
for (t = 1; t <= m.vetnum; t++) {
printf("%d ", ShortPathwet[t]);
}
printf("\n\n");
//打印最短路径
for (t = 1; t <= m.vetnum; t++) {
p = t;
if (t != v0) {
printf("%d%s", t, m.vets[t].name);
for (w = 1; w <= m.vetnum; w++) {
if (Pathside[p] != 0) {
printf("<--%d%s", Pathside[p], m.vets[p].name);
p = Pathside[p];
}
}
printf("<--%d%s", v0, m.vets[v0].name);
printf("\n总路线长为%d米\n\n", ShortPathwet[t]);
}
}
}
//6.主页
void menu() {
printf(" │ 承德避暑山庄 │\n");
printf(" │ 菜 单 选 择 │\n");
printf(" │ ****************************************************│\n");
printf(" │ ******************1.主页****************************│\n");
printf(" │ ******************2.查看游览路线 *******************│\n");
printf(" │ ******************3.查询景点间最短路径**************│\n");
printf(" │ ******************4.查询景点间所有路径**************│\n");
printf(" │ ******************5.景点介绍************************│\n");
printf(" │ ******************6.景区地图************************│\n");
printf(" │ ******************0.退出****************************│\n");
printf(" │ *************************************************** │\n");
}
//7.输出图的邻接矩阵的值
void printmapstr(mapstr m) {
int i, j, k = 0;
for (i = 1; i <= vertex; i++) {
if (m.vets[i].number != -1)
printf("%6d", i);
}
printf("\n");
for (i = 1; i <= m.vetnum; i++) {
for (j = 1; j <= m.vetnum; j++) {
if (m.mat[i][j].wet == Infinity)
printf(" **** ");
else
printf("%6d", m.mat[i][j].wet);
k++;
if (k % m.vetnum == 0)
printf("\n");
}
}
}
//8.弗洛伊德算法
void floyd(mapstr m) {
int i, j, k;
for (i = 1; i <= vertex; i++) { //将图的邻接矩阵赋值给 shortest二维数组,将矩阵pathh全部初始化为-1
for (j = 1; j <= vertex; j++) {
shortest[i][j] = m.mat[i][j].wet;
pathh[i][j] = j;
}
}
int ii, jj, k1 = 0;
for (ii = 1; ii <= vertex; ii++)
printf("%6d", ii); //横着标号1到11
printf("\n");
for (ii = 1; ii <= vertex; ii++) {
printf("%d", ii);
for (jj = 1; jj <= vertex; jj++) {
printf("%6d", pathh[ii][jj]);
k1++;
if (k1 % vertex == 0)
printf("\n");
}
}
printf("\n\n\n");
for (k = 1; k <= vertex; k++) { //核心操作,完成了以k为中间点对所有的顶点对(i,j)进行检测和修改
for (i = 1; i <= vertex; i++) {
for (j = 1; j <= vertex; j++) {
if (shortest[i][j] > shortest[i][k] + shortest[k][j]) {
shortest[i][j] = shortest[i][k] + shortest[k][j];
pathh[i][j] = pathh[i][k]; //记录一下所走的路 //P数组用来存放前驱顶点
}
}
}
}
}
//9.输出数组
void printarray() {
int i, j, k = 0;
for (i = 1; i <= vertex; i++)
printf("%6d", i); //横着标号0-11
printf("\n");
for (i = 0; i <= vertex; i++) {
printf("%d", i); //竖着标号0-11
for (j = 1; j <= vertex; j++) {
printf("%6d", pathh[i][j]);
k++;
if (k % vertex == 0)
printf("\n");
}
}
printf("\n\n\n");
}
//10.输出最短路径
void display(mapstr m, int i, int j) {
int a, b;
a = i, b = j;
printf("您要查询的两景点间最短路径:\n\n");
printf("%d%s", a, m.vets[a].name);
while (pathh[i][j] != b) {
printf("-->%d%s", pathh[i][j], m.vets[pathh[i][j]].name);
i = pathh[i][j];
}
printf("-->%d%s\n\n", b, m.vets[b].name);
printf("%s-->%s的最短路径是:%d米\n\n", m.vets[a].name, m.vets[b].name, shortest[a][b]);
}
//11任意两点间距离(16-19)
int shortdistance(mapstr m) {
int i, j;
printf("请输入要查询的两个景点的数字编号(用空格隔开)\n");
scanf("%d %d", &i, &j);
if (i < 0 || i > vertex || j < 0 || j > vertex) {
printf("输入信息有误!\n\n");
printf("请输入要查询的两个景点的数字编号(用空格隔开)\n");
scanf("%d %d", &i, &j);
} else {
floyd(m);
printarray();
display(m, i, j);
}
return 1;
}
//12显示所有景点信息
void compusinfor(mapstr m) {
int i;
printf(" \n\n编号 景点名称 简介\n");
printf("*************************************************************************\n");
for (i = 1; i <= vertex; i++) {
if (m.vets[i].number != -1)
printf("%-10d%-25s%-70s\n", m.vets[i].number, m.vets[i].name, m.vets[i].intro);
}
printf("*************************************************************************\n");
}
//13地图
void schoolmap() {
printf(" ---------------------------------承德避暑山庄地图----------------------------------------------\n");
printf("````````````````````````````````````````````````````````````````````````````````````````````````\n");
printf("````````````````````````````````````````````````````````````````````````````````````````````````\n");
printf("````````````````````````````````````````````````````````````````````南山积雪````````````````````\n");
printf("````````````````````````````````````````````````````````````````````````````````````````````````\n");
printf("``````````````````````````````````````````````````````````````````````````````陆合塔````````````\n");
printf("````````````````````````````````````````````````````````````````````````````````````````````````\n");
printf("`````````````````````````````````````````````````文津阁`````````````````````````````````````````\n");
printf("``````四面云山``````````````````````````````````````````````````````````````````万树园``````````\n");
printf("````````````````````````````````````````````````````````````````````````````````````````````````\n");
printf("````````````````````````````````````````````````````````````````````````````````````````````````\n");
printf("````````````````````````````````芳园居`````````````````月色江声`````````````````````````````````\n");
printf("```````````````````````````````环碧`````````````````````````````````````````````````````````````\n");
printf("````````````````````````````````````````````````````````````````````````````````````````````````\n");
printf("``````````````````````````````水心檞````````````````````````````````````````````````````````````\n");
printf("````````````````````````````````````````````````````````````````````````````````````````````````\n");
printf("``````````````````正宫````````````````````````````````````````````文园狮子林````````````````````\n");
printf("````````````````````````````````````````````````````````````````````````````````````````````````\n");
printf("```````````````````丽正门``````````````````````````````德汇门```````````````````````````````````\n");
printf("\n\n");
printf("\n 西<---|--->东 \n");
}
//14用户界面
void mainwork() {
menu();
int choice;
campus = initmap();
do {
printf("请输入你的选择:");
scanf("%d", &choice);
switch (choice) {
case 1:
menu();
break;
case 2:
shortestpath(campus);
break;
case 3:
shortdistance(campus);
break;
case 4:
allpath(campus);
break;
case 5:
compusinfor(campus);
break;
case 6:
schoolmap();
break;
case 0:
printf("谢谢使用\n");
break;
default:
printf("未找到该功能,请输入有效选项!\n");
break;
}
} while (choice);
}
int main() {
mainwork();
return 0;
}