“校园导航”实验

一、实验目的与要求:

(一)实验目的

1、掌握图形结构的(邻接矩阵)输入方法

2、掌握图形结构的说明、创建以及图的存储表示

3、掌握最短路径算法原理

4、掌握最短路径的实现方法

(二)实验要求

1、熟悉C/C++语言编程

2、熟练使用C/C++语言实现图形结构的说明、创建以及图的存储表示

二、实验内容:

校园导航问题

(一)问题描述:

设计校园的平面图,至少包括8个以上的场所,每两个场所间可以有不同的路,且路长也可能不同。

1.(必做)找出从一个场所出发到达其它所有场所的最佳路径(最短路径)。

2.(选做)找出从任意场所到达另一场所的最佳路径(最短路径)。

(二) 基本要求

1、设计校园平面图,在校园景点选8个以上的景点。以图中顶点表示校园内各景点,存放景点名称、代号、简介等信息;以边表示路径,存放路径长度等有关信息。

2、为来访客人提供图中任意景点相关信息的查询。

3、为来访客人提供任意景点的问路查询。

(如:从学校1号门到各景点的最佳路线及距离;主楼到湖边餐厅的最佳路线及距离。)

4、界面要求:有合理提示;每个功能可以设立菜单;根据提示,可以完成相关的功能要求

3、熟练应用图形数据结构编程实现最短路径的算法

三、实验步骤与过程:

[介绍实验过程、算法描述及解题思路等]

[实验代码清单(要有注释语句)]

实验过程:

    先对实验要求进行一个脑中的呈现。如:输出格式,菜单页面,所需算法等等。再根据所想的算法进行代码实现。最后,对输出格式进行修改及完善即可。

算法描述:

  Dijkstra算法是一个基于「贪心」、「广度优先搜索」、「动态规划」求一个图中一个点到其他所有点的最短路径的算法,时间复杂度O(n^2)。 (可以解决问题一)

  Floyd算法是一个基于「贪心」、「动态规划」求一个图中所有点到所有点最短路径的算法,时间复杂度O(n^3)。(可以解决问题二)

解题思路:

  看到第一问和第二问就很容易想到了Dijkstra算法和Floyd算法去实现,所以就去实现这个两个算法即可。

代码呈现:

功能一:对各个场所进行简单描述

注:对于这个其实也可以创建一个顶点的结构体,包含char str[MAX]去存放场所信息。

//各个场所的功能函数
void feature()
{
	hint();
	printf("请输入场所编号: ");
	int num;
	scanf("%d",&num);
	switch (num) 
	{
		case 1:
			printf("宿舍有公共厨房,公共阅览室,公共厕所\n\n");
			break;		
		case 2:
			printf("一食堂有丰富的菜品\n\n");
			break;	
		case 3:
			printf("湖滨餐厅有中西方菜品\n\n");
			break;	
		case 4:
			printf("图书馆设施好,学习氛围浓烈\n\n");
			break;	
		case 5:
			printf("实验楼有实验室以及机房\n\n");
			break;			
		case 6:
			printf("可以在里面举办一些晚会\n\n");
			break;	
		case 7:
			printf("老师办公区域\n\n");
			break;	
		case 8:
			printf("看主楼的最好位置\n\n");
			break;	
		default:
			printf("无该场所编号,请重新输入场所编号\n\n");
			break;		
	}
}

功能二:找出从一个场所出发到达其它所有场所的最佳路径(dilkstra算法)

//取最小值的函数 
int getMin(int* d,int* s,Graph* G)
{
	int min=MAX;
	int index;
	for(int i=0;i<G->vexNum;i++)
	{
		if(!s[i] && d[i]<min)	//比较权值 
		{
			min=d[i];
			index=i;
		}
	}
	return index;
}

void dijkstra(Graph* G,int index)	//index为目标节点 
{
	/*
	最短路径(dijkstra算法):
		准备辅助数组: 
		S数组:记录了目标顶点到其他顶点的最短路径是否求得
		P数组:记录了目标顶点到其他顶点的最短路径的前驱节点
		D数组:记录了目标顶点到其他顶点的最短路径长度 
	*/
	int* s=(int*)malloc(sizeof(int)*G->vexNum); 
	int* p=(int*)malloc(sizeof(int)*G->vexNum);
	int* d=(int*)malloc(sizeof(int)*G->vexNum);
	//对辅助数组进行初始化 
	for(int i=0;i<G->vexNum;i++)
	{
		if(G->arcs[index][i]>0 && G->arcs[index][i]!=MAX)
		{
			d[i]=G->arcs[index][i];
			p[i]=index;
		}
		else
		{
			d[i]=MAX;
			p[i]=-1;
		}
		if(i==index)
		{
			s[i]=1;
			d[i]=0;
		}
		else
		{
			s[i]=0;
		}
	}  
	for(int i=0;i<G->vexNum-1;i++)
	{
		int index=getMin(d,s,G);
		s[index]=1;		//代表已经找到了最小路径 
		for(int j=0;j<G->vexNum;j++)
		{
			if(!s[j] && d[index]+G->arcs[index][j]<d[j])	//判断是否要重新更新d数组中的最小路径 
			{
				d[j]=d[index]+G->arcs[index][j];	 
				p[j]=index;		//更改 
			}
		} 
	}
	//输出到达个点的最短路径 
	for(int i=0;i<=G->vexNum-1;i++)
	{
	    int j=i;
		printf("%s <—— ",G->vexs[i].name);	//最后打印所到场所
	    while(j!=index)
		{
			j=p[j];
	        if(j!=index)
	        {
				printf("%s <——  ",G->vexs[j].name);	
			}
		}  	 
		printf("%s",G->vexs[index].name);
		printf("\n%s --> %s的最短路径大小为:%d分钟",G->vexs[index].name,G->vexs[i].name,d[i]);
		printf("\n");
	}
}

功能三:找出从一个场所出发到达其它所有场所的最佳路径(floyd算法)

/*
	floyd算法(3 for:[n*n*n]):求每一对顶点之间的最短路径
	d数组:保存了两点的最短路径长度(权值)
	p数组:保存了两点之间最短路径前驱
	核心:试探法,通过加入不同的点进行中转,选择出最优解
*/
void floyd(Graph* G,int f,int n)
{
	int d[G->vexNum][G->vexNum];
	int p[G->vexNum][G->vexNum];
	//数组的初始化
	for(int i=0;i<G->vexNum;i++)
	{
		for(int j=0;j<G->vexNum;j++)
		{
			d[i][j]=G->arcs[i][j];
			if(G->arcs[i][j]>0 && G->arcs[i][j]!=MAX)	//两个顶点之间有弧
			{
				p[i][j]=i;	//有弧指向前继顶点
			}
			else
			{
				p[i][j]=-1;
			}
		}
	}
	/*
	进行试探法:第一个循环代表着加入的顶点进行试探
			   后两个循环代表着每一对顶点
	*/
	for(int i=0;i<G->vexNum;i++)
	{
		for(int j=0;j<G->vexNum;j++)
		{
			for(int k=0;k<G->vexNum;k++)
			{
				if(d[j][i]+d[i][k]<d[j][k])
				{
					d[j][k]=d[j][i]+d[i][k];
					p[j][k]=p[i][k];
				}
			}	
		}
	}
	printf("%s --> %s的最小路径为:%d分钟\n",G->vexs[f].name,G->vexs[n].name,d[f][n]);
	int l=p[f][n];  // l作为中间变量用来接受最短路径中的父亲节点 
	printf("最短路径为:  %s",G->vexs[f].name);   // 输出最短路径 
	int temp=0;		//创建一个中间变量,便于储存l值
	while(l!=f)  
	{
		printf(" --> %s",G->vexs[l].name);
		if(l==temp)		//当后一个l等于前一个l时,循环截至
			break;
		l=p[l][n];  //  不断更新l节点 
		temp=l;
	}
		printf(" --> %s\n\n",G->vexs[n].name);

}

完整代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h>

/*图顶点之间不通,那么邻接矩阵的值为MAX
  如果顶点是自己本身,那么值为0*/
#define MAX 32767

int system(const char *command);//

//结构体定义顶点
typedef struct Vertex  
{
	int num;  //顶点编号 
	char name[MAX];  //顶点名称 
}Vertex;     

typedef struct Graph{
	Vertex vexs[9];		//顶点
	int** arcs;		//权重(使用**目的表示矩阵)
	int vexNum;
	int arcsNum;
}Graph;

//初始化一个图
Graph* initGraph(int vexNum)
{
	Graph* G=(Graph*)malloc(sizeof(Graph));
	G->arcs=(int**)malloc(sizeof(int*) *vexNum);
	for(int i=0;i<vexNum;i++)
	{
		G->arcs[i]=(int*)malloc(sizeof(int) *vexNum);
	}
	for(int i=1;i<=G->vexNum;i++) 
	 	G->vexs[i].num=i;    //初始化顶点编号为1-10 
	G->vexNum=vexNum;
	G->arcsNum=0;
	strcpy(G->vexs[0].name,"宿舍");
	strcpy(G->vexs[1].name,"一食堂");
	strcpy(G->vexs[2].name,"湖滨餐厅");
	strcpy(G->vexs[3].name,"图书馆");
	strcpy(G->vexs[4].name,"二号实验楼");
	strcpy(G->vexs[5].name,"礼堂");
	strcpy(G->vexs[6].name,"主楼");
	strcpy(G->vexs[7].name,"一号门");
	return G;
}

//构建一个图
void createGraph(Graph* G,int* arcs)
{
	for(int i=0;i<G->vexNum;i++)
	{
		for(int j=0;j<G->vexNum;j++)
		{
			G->arcs[i][j]=*(arcs+i*G->vexNum+j);
			if(G->arcs[i][j]!=0&&G->arcs[i][j]!=MAX)
			{
				G->arcsNum++;
			}
		}
	}
	G->arcsNum=G->arcsNum/2;
}

//取最小值的函数 
int getMin(int* d,int* s,Graph* G)
{
	int min=MAX;
	int index;
	for(int i=0;i<G->vexNum;i++)
	{
		if(!s[i] && d[i]<min)	//比较权值 
		{
			min=d[i];
			index=i;
		}
	}
	return index;
}

void dijkstra(Graph* G,int index)	//index为目标节点 
{
	/*
	最短路径(dijkstra算法):
		准备辅助数组: 
		S数组:记录了目标顶点到其他顶点的最短路径是否求得
		P数组:记录了目标顶点到其他顶点的最短路径的前驱节点
		D数组:记录了目标顶点到其他顶点的最短路径长度 
	*/
	int* s=(int*)malloc(sizeof(int)*G->vexNum); 
	int* p=(int*)malloc(sizeof(int)*G->vexNum);
	int* d=(int*)malloc(sizeof(int)*G->vexNum);
	//对辅助数组进行初始化 
	for(int i=0;i<G->vexNum;i++)
	{
		if(G->arcs[index][i]>0 && G->arcs[index][i]!=MAX)
		{
			d[i]=G->arcs[index][i];
			p[i]=index;
		}
		else
		{
			d[i]=MAX;
			p[i]=-1;
		}
		if(i==index)
		{
			s[i]=1;
			d[i]=0;
		}
		else
		{
			s[i]=0;
		}
	}  
	for(int i=0;i<G->vexNum-1;i++)
	{
		int index=getMin(d,s,G);
		s[index]=1;		//代表已经找到了最小路径 
		for(int j=0;j<G->vexNum;j++)
		{
			if(!s[j] && d[index]+G->arcs[index][j]<d[j])	//判断是否要重新更新d数组中的最小路径 
			{
				d[j]=d[index]+G->arcs[index][j];	 
				p[j]=index;		//更改 
			}
		} 
	}
	//输出到达个点的最短路径 
	for(int i=0;i<=G->vexNum-1;i++)
	{
	    int j=i;
		printf("%s <—— ",G->vexs[i].name);	//最后打印所到场所
	    while(j!=index)
		{
			j=p[j];
	        if(j!=index)
	        {
				printf("%s <——  ",G->vexs[j].name);	
			}
		}  	 
		printf("%s",G->vexs[index].name);
		printf("\n%s --> %s的最短路径大小为:%d分钟",G->vexs[index].name,G->vexs[i].name,d[i]);
		printf("\n");
	}
}

/*
	floyd算法(3 for:[n*n*n]):求每一对顶点之间的最短路径
	d数组:保存了两点的最短路径长度(权值)
	p数组:保存了两点之间最短路径前驱
	核心:试探法,通过加入不同的点进行中转,选择出最优解
*/
void floyd(Graph* G,int f,int n)
{
	int d[G->vexNum][G->vexNum];
	int p[G->vexNum][G->vexNum];
	//数组的初始化
	for(int i=0;i<G->vexNum;i++)
	{
		for(int j=0;j<G->vexNum;j++)
		{
			d[i][j]=G->arcs[i][j];
			if(G->arcs[i][j]>0 && G->arcs[i][j]!=MAX)	//两个顶点之间有弧
			{
				p[i][j]=i;	//有弧指向前继顶点
			}
			else
			{
				p[i][j]=-1;
			}
		}
	}
	/*
	进行试探法:第一个循环代表着加入的顶点进行试探
			   后两个循环代表着每一对顶点
	*/
	for(int i=0;i<G->vexNum;i++)
	{
		for(int j=0;j<G->vexNum;j++)
		{
			for(int k=0;k<G->vexNum;k++)
			{
				if(d[j][i]+d[i][k]<d[j][k])
				{
					d[j][k]=d[j][i]+d[i][k];
					p[j][k]=p[i][k];
				}
			}	
		}
	}
	printf("%s --> %s的最小路径为:%d分钟\n",G->vexs[f].name,G->vexs[n].name,d[f][n]);
	int l=p[f][n];  // l作为中间变量用来接受最短路径中的父亲节点 
	printf("最短路径为:  %s",G->vexs[f].name);   // 输出最短路径 
	int temp=0;		//创建一个中间变量,便于储存l值
	while(l!=f)  
	{
		printf(" --> %s",G->vexs[l].name);
		if(l==temp)		//当后一个l等于前一个l时,循环截至
			break;
		l=p[l][n];  //  不断更新l节点 
		temp=l;
	}
		printf(" --> %s\n\n",G->vexs[n].name);

}

void menu()
{
	printf("**************************************************************\n");
	printf("		深圳北理莫斯科大学校园导航         	          \n");
	printf("**************************************************************\n");
	printf("****  1.查询找出从一个场所出发到达其它所有场所的最佳路径  ****\n");
	printf("****  2.查询找出从任意场所到达另一场所的最佳路径          ****\n");
	printf("****  3.查询各个场所的功能                                ****\n");
	printf("****  4.清屏                            				  ****\n");
	printf("****  0.退出程序                                          ****\n");
	printf("**************************************************************\n");
}

void hint()
{
	printf("1-宿舍        2-一食堂  3-湖滨餐厅  4-图书馆\n");
	printf("5-二号实验楼  6-礼堂    7-主楼      8-一号门\n");
}

//各个场所的功能函数
void feature()
{
	hint();
	printf("请输入场所编号: ");
	int num;
	scanf("%d",&num);
	switch (num) 
	{
		case 1:
			printf("宿舍有公共厨房,公共阅览室,公共厕所\n\n");
			break;		
		case 2:
			printf("一食堂有丰富的菜品\n\n");
			break;	
		case 3:
			printf("湖滨餐厅有中西方菜品\n\n");
			break;	
		case 4:
			printf("图书馆设施好,学习氛围浓烈\n\n");
			break;	
		case 5:
			printf("实验楼有实验室以及机房\n\n");
			break;			
		case 6:
			printf("可以在里面举办一些晚会\n\n");
			break;	
		case 7:
			printf("老师办公区域\n\n");
			break;	
		case 8:
			printf("看主楼的最好位置\n\n");
			break;	
		default:
			printf("无该场所编号,请重新输入场所编号\n\n");
			break;		
	}
}

int main()
{
	//打印学校图(注:需要把照片与代码放在用一个文件夹下)
	char pic_name[80]="school.PNG";
	char cmd[100];
	sprintf(cmd,pic_name);
	system(cmd);
	Graph* G=initGraph(8);
	int arcs[8][8]={	//邻接矩阵(权重(按走到该建筑的时间创立))
		0,3,6,6,10,5,MAX,4,
		3,0,4,MAX,MAX,MAX,MAX,MAX,
		6,4,0,1,6,MAX,MAX,MAX,
		6,MAX,1,0,5,MAX,MAX,MAX,
		10,MAX,6,5,0,MAX,3,MAX,
		5,MAX,MAX,MAX,MAX,0,1,2,
		MAX,MAX,MAX,MAX,3,1,0,1,
		4,MAX,MAX,MAX,MAX,2,1,0
		};
	printf("学校的平面图为:\n");
	createGraph(G,(int*)arcs);
	int input;
	do
	{
		Sleep(1000);	//延迟输出1s,方便查看
		menu();
		printf("请输入所需要的功能: ");
		scanf("%d",&input);
		switch (input) {
			case 1:
			{	
				int n;
				hint();	
				printf("请输入场所编号: ");
				scanf("%d",&n);
				if(n<1||n>8)
				{
					printf("无该场所编号,请重新输入\n\n");
					break;
				}
				printf("\n从该场所到各个场所的最短路径依次是:\n");
				dijkstra(G,n-1);
				printf("\n");		
				break;
			}
			case 2:
			{
				int a,b;
				hint();	
				printf("请输入想从哪个场所到哪个场所的编号\n");
				printf("开始场所: ");
				scanf("%d",&a);
				printf("目的地场所: ");
				scanf("%d",&b);
				if((a<1||a>8)&&(b<1||b>8))
				{
					printf("无该场所编号,请重新输入\n\n");
					break;
				}
				floyd(G,a-1,b-1);			
				break;	
			}
			case 3:
			{	
				feature();
				break;
			}
			case 4:
			{
				system("cls");
				break;
			}
			case 0:
			{
				printf("退出程序\n");
				break;
			}
			default:
			{	
				printf("无该功能,请重新输入所需的功能\n\n");		
				break;
			}
		}
	}while(input);	
	return 0;
}
 

四、实验结果及数据处理分析: 

 

    根据运行结果,发现此代码实现了该实验的功能。(为了更好的呈现结果这里将清屏功能删除,在实际代码中可实现清屏功能。) 

猜你喜欢

转载自blog.csdn.net/Lookdrama/article/details/127634282