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
#include
#include
#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,和老师给的答案一样,并且可以给出从起点到其余各点的疲劳值。