数据结构课程设计(五)---行车路线

1、任务简述:
小明和小芳出去乡村玩,小明负责开车,小芳来导航。
小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走s公里小明会增加s2的疲劳度。
例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2)2+2+22=16+2+4=22。
现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。

要求:
输入格式:
  输入的第一行包含两个整数n, m,分别表示路口的数量和道路的数量。路口由1至n编号,小明需要开车从1号路口到n号路口。
接下来m行描述道路,每行包含四个整数t, a, b, c,表示一条类型为t,连接a与b两个路口,长度为c公里的双向道路。其中t为0表示大道,t为1表示小道。保证1号路口和n号路口是连通的。
输出格式
输出一个整数,表示最优路线下小明的疲劳度。
样例输入
6 7
1 1 2 3
1 2 3 2
0 1 3 30
0 3 4 20
0 4 5 30
1 3 5 6
1 5 6 1
样例输出
76
样例说明
  从1走小道到2,再走小道到3,疲劳度为52=25;然后从3走大道经过4到达5,疲劳度为20+30=50;最后从5走小道到6,疲劳度为1。总共为76。
课程设计要求:
(1)要求从文本文件中输入;
(2)采用适当的数据结构存储由输入数据中的道路所形成的图结构;
(3)编写尽可能优的算法,处理好连续走小道造成的疲劳值的指数增长(提示:基于迪杰斯特拉算法进行改进即可完成本题);
(4)除严格按题目要求进行输出以外,还要求输出最优路线的路径,以及从出发点到各个点的最小疲劳值。
2、算法描述:
数据结构:
typedef struct MyGraph//用邻接矩阵来表示
{
int type; //0表示无向网,1表示有向网
int arcnum; //边的数量
int vexnum; //顶点的数量
int **A; //大街道邻接矩阵
int **B; //小街道邻接矩阵
}GH;

因为既可以走大路,又可以走小路,所以把两种路分开来看,各用一个数组存储。由于连续走小路时,疲劳值增加连续小路的总长度的平方,所以首先用FLoyd算法将走小路进行归并一下,以后在计算的时候就只需要考虑三种情况:1.大路+大路,2.小路+大路,3.大路+小路。用两个数组dis和dis0分别存储大路和小路到达i点时的最小疲劳值,最后取n处的两者的最小值

3、源代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define maxx 9999

typedef struct MyGraph//用邻接矩阵来表示 
{
	int type; //0表示无向网,1表示有向网
	int arcnum; //边的数量 
	int vexnum; //顶点的数量 
	int **A; //大街道邻接矩阵 
	int **B; //小街道邻接矩阵 
}GH;

void creatgraph(GH *G);  //以邻接矩阵的形式创建图
void showgraph(GH *G);   //以邻接矩阵的形式显示图
void pilao(GH *G,int start,int end,int n);  

int main()
{
	int start,end;
	GH G;
	system("color 1E");
	printf("------------------------------081810221朱林昊------------------------------\n");
	printf("\n------------------------------行车路径------------------------------\n\n");
	creatgraph(&G);//创建并展示图
	printf("\n请输入你的起点:");
	scanf("%d",&start);
	printf("\n请输入你的终点:");
	scanf("%d",&end);
	pilao(&G,start-1,end-1,G.vexnum);
	return 0;
}

void creatgraph(GH *G)//创建图 
{
	FILE *fp;
	int i,j;
	int k,m,n,t;
	int a,b,c;
	fp=fopen("zlh.txt","rb");
	if(!fp)
	{
		printf("Can not open file!!!\n");
		exit(0);
	}
	fscanf(fp,"%d %d",&n,&m);
	G->vexnum=n;
	G->arcnum=m;
	G->type=1;  //题目中小明从1到n,所以我认为应该理解为有向图 
	G->A=(int **)malloc(n*sizeof(int *));
	G->B=(int **)malloc(n*sizeof(int *));
	for(i=0;i<n;i++)
	{
		G->A[i]=(int *)malloc(n*sizeof(int));
		G->B[i]=(int *)malloc(n*sizeof(int));
		for(j=0;j<n;j++)
		{
			G->A[i][j]=maxx;
			G->B[i][j]=maxx;
		}
		G->A[i][i]=0;
		G->B[i][i]=0;
	}
	for(i=0;i<m;i++)
	{
		fscanf(fp,"%d %d %d %d",&t,&a,&b,&c);
		if(!t)
		{
			G->A[a-1][b-1]=c;
			G->A[b-1][a-1]=c;
		}
		else
		{
			G->B[a-1][b-1]=c;
			G->B[b-1][a-1]=c;
		}
	}
	showgraph(G);      //显示图
	for(k=0;k<n;k++)   //采用floyd求每个点间小道的最短距离 
		for(i=0;i<n;i++)
			for(j=0;j<n;j++)
				if(G->B[i][j]>G->B[i][k]+G->B[k][j])
					G->B[i][j]=G->B[i][k]+G->B[k][j]; 
	fclose(fp);//关闭数据文件	
}

void showgraph(GH *G)//显示图 
{
	int i,j;
	for(i=0;i<G->vexnum;i++)
	{
		printf("%d号路口",i+1);
		for(j=0;j<G->vexnum;j++)
		{
			if(G->A[i][j]<maxx&&G->A[i][j])
				printf("-->%d号路口(走大道,距离:%d)",j+1,G->A[i][j]);
			else if(G->B[i][j]<maxx&&G->B[i][j])
				printf("-->%d号路口(走小道,距离:%d)",j+1,G->B[i][j]);
		}
		printf("\n\n");
	}
	printf("\n");
}

void pilao(GH *G,int start,int end,int n)
{
	int i,k,head=0,rear=0;
	int dis[n],dis1[n],v,f[n];
	rear++;
	int q[n];
	for(i=0;i<n;i++)
	{
		f[i]=0;
		dis[i]=maxx;
		dis1[i]=maxx;
	} 
	f[start]=1;
	q[0]=start;
	dis[start]=0;
	dis1[start]=0;
	while(head!=rear)
	{
		k=q[head++];
		f[k]=0;
		if(head>=n)
			head=0;
		for(i=0;i<n;i++)
		{
			if(i==k)
				continue;
			v=G->A[k][i];
			if(dis[i]>dis[k]+v)//走大道 
			{
				dis[i]=dis[k]+v;
				if(!f[i])
				{
					f[i]=1;
					q[rear++]=i;
					if(rear>=n)
						rear=0;
				}
			}
			if(dis[i]>dis1[k]+v)
			{
				dis[i]=dis1[k]+v;
				if(!f[i])
				{
					f[i]=1;
					q[rear++]=i;
					if(rear>=n)
						rear=0;
				}
			}
			if(G->B[i][k]<maxx)
			{
				v=G->B[i][k]*G->B[i][k];
				if(dis1[i]>dis[k]+v)
				{
					dis1[i]=dis[k]+v;
					if(!f[i])
					{
						f[i]=1;
						q[rear++]=i;
						if(rear>=n)
							rear=0;
					}
				}
			}
		}
	} 
	printf("\n从%d号路口到%d号路口的最小疲劳值:%d\n",start+1,end+1,dis[end]>dis1[end]?dis1[end]:dis[end]);
	printf("\n------------------------------起点%d号路口到各路口的最小疲劳值------------------------------\n",start+1); 
	for(i=0;i<n;i++)
		printf("\n从%d号路口到%d号路口的最小疲劳值:%d\n",start+1,i+1,dis[i]>dis1[i]?dis1[i]:dis[i]);
}//共158行

4、运行结果
在这里插入图片描述
5、总结
性能分析:
时间复杂度:采用了一些经典的算法,floyed算法,其时间复杂度为O(n3),spfa算法,其时间复杂度为O(ne),又因为边的数量最多为n(n-1)–考虑有向图,因此最终的时间复杂度O(n3)
空间复杂度:O(n),用了数组dis和dis1来存储疲劳值
遇到的问题与解决方法:
一开始我直接使用floyed算法和dijkstra算法来实现,但是只能得到局部最优的答案,所以我查阅了相关的文献资料,学习了spfa算法,并且实现,便可以达到正确的答案。
心得体会:
运行结果正确,1号路口到6号路口的疲劳值为76,和老师给的答案一样,并且可以给出从起点到其余各点的疲劳值。

猜你喜欢

转载自blog.csdn.net/zhulinhao/article/details/106898619