行车路线
[问题描述]
小明和小芳出去乡村玩,小明负责开车,小芳来导航。
小芳将可能的道路分为大道和小道。大道比较好走,每走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)除严格按题目要求进行输出以外,还要求输出最优路线的路径,以及从出发点到各个点的最小疲劳值。
数据结构
利用图的邻接矩阵进行存储
typedef struct ArceCell
{
VRType adj;
VRType bs;//存储大道,小道
}ArcCell , AdjMatrix[ MAX_VERTEX_NUM ][ MAX_VERTEX_NUM ];
typedef struct
{
VertexType vexs[MAX_VERTEX_NUM];//顶点向量
AdjMatrix arcs;
int vexnum , arcnum;
}MGraph;
算法设计思想
利用图的邻接矩阵来存储路口和每条路的疲劳值;
设置数组存储每个路径的前一个结点。
利用迪杰斯特拉算法,设置循环,遍历矩阵,每次将当前路口的最小疲劳值置为最大,从路口1开始遍历没有走过的路口,若u-v这条路为小路,若u前面与1号路口间有小道相连,则求它们和的平方,若还有大道相连,则直接相加,再取两次的疲劳值中小的值与前面的结果相加,即为这个路口到1号结点的最小疲劳值,再重复上述步骤,直到遍历所有路口为止。
测试数据和结果
测试数据:
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
结果:
算法时间复杂度
void ShortestPath_DIJ( int s, MGraph G )
时间复杂度:O(n²)
代码如下:
我注释应该写的挺全的,可能略有些繁琐,如果有不明白的可以留言问我。
#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include<fstream>
#include <iomanip>
using namespace std;
#define INFINITY INT_MAX
#define MAX_VERTEX_NUM 80//最大路口数
#define VRType long long
#define VertexType int
int road[ MAX_VERTEX_NUM];
int num=0;
long long now_p[MAX_VERTEX_NUM];//存储储存出发点到各个点的疲劳值
bool if_visited[MAX_VERTEX_NUM];// 存储当前路口是否已被访问
long double bestLength = INFINITY;// 最短路径初始化为无穷大
long long small_rode[MAX_VERTEX_NUM] ;//每个结点到出发点的小道路径之和
long long square_road[MAX_VERTEX_NUM];
long long big_road[MAX_VERTEX_NUM];//每个结点到出发点的大道路径之和
long long pass_road[MAX_VERTEX_NUM];//存储当前的疲劳值
int pre[MAX_VERTEX_NUM ];
typedef struct ArceCell
{
VRType adj;
VRType bs;//存储大道,小道
}ArcCell , AdjMatrix[ MAX_VERTEX_NUM ][ MAX_VERTEX_NUM ];
typedef struct
{
VertexType vexs[MAX_VERTEX_NUM];
AdjMatrix arcs;
int vexnum , arcnum;
}MGraph;
//创建图
void CreateGraph( MGraph &G )
{
int i, j;
long long a[4];
int k1, k2;
ifstream read_in;
read_in.open( "text5.txt" );
if ( !read_in )
{
cout<< "文件读取失败!" <<endl;
return;
}
read_in >> G.vexnum;
read_in >> G.arcnum;
for ( i = 1; i <= G.vexnum ; i++ )
{
for ( j = 1; j <= G.vexnum; j++)
{
G.arcs[i][j].adj = INFINITY;
}
}
for( i = 1; i <= G.vexnum; i++)
{
G.vexs[i]=i;
}
for( i = 1; i <= G.arcnum; i++ )
{
for ( j = 0; j < 4; j++ )
{
read_in >> a[j];
}
k1 = a[1];
k2 = a[2];
if( a[0]==0 )//t为0表示大道
{
G.arcs[k1][k2].bs=0;
G.arcs[k1][k2].adj = a[3];
G.arcs[k2][k1].bs=0;
G.arcs[k2][k1].adj = a[3];
}
else if( a[0]==1 )//t为1表示小道
{
G.arcs[k1][k2].bs=1;
G.arcs[k1][k2].adj = a[3];
G.arcs[k2][k1].bs=1;
G.arcs[k2][k1].adj = a[3];
}
}
cout<< "无向图创建成功!" <<endl;
cout<< "顶点数: " << G.vexnum <<endl;
cout<< "边数:" << G.arcnum <<endl;
cout<< "顶点: ";
for ( i = 1; i <= G.vexnum; i++ )
{
cout<< G.vexs[i] <<" ";
}
cout<<endl<<"******************邻接矩阵****************"<<endl;
k1 = 0;
for ( i = 1; i <= G.vexnum ; i++ )
{
for ( j = 1; j <= G.vexnum; j++)
{
if( G.arcs[i][j].adj==INFINITY )
printf( "%-2d ", k1 );
else
printf( "%-2lld ", G.arcs[i][j].adj );
}
cout<<endl;
}
cout<<"******************************************"<<endl;
read_in.close();
}
long long square(long long x)
{
return x*x;
}
//利用迪杰斯特拉算法求疲劳值
void ShortestPath_DIJ( int s, MGraph G )
{
int i, v;
long long x, y, Shortest;
for( i=0; i<MAX_VERTEX_NUM; i++ )
{
big_road[i] = INFINITY;
small_rode[i] = INFINITY;
square_road[i] = INFINITY;
pass_road[i] = INFINITY;
if_visited[i] = false;
pre[i] = 0;
}
pass_road[s]=0;
big_road[s]=0;
small_rode[s]=0;
while(1)
{
int u=0;
Shortest = INFINITY;
for( i=1; i<=G.vexnum; i++)//更新最短距离
{
if(if_visited[i]==false&&pass_road[i]<Shortest)
{
u=i;
Shortest=pass_road[i];
}
}
if( u==0 )//若未更新
break;
road[num] = u;
num++;
if_visited[u] = true;//将u号路口置为已被访问
//开始主循环,每次求得路口到1号路口的最短路径
for ( v=1; v<=G.vexnum; v++ )
{
if( G.arcs[u][v].adj!=INFINITY && if_visited[v]==false )
{
x = INFINITY;
y = INFINITY;
if ( G.arcs[u][v].bs==1 )//小道
{
if ( small_rode[u]!=INFINITY )//如果这个路口和1号路口有小道相连
{
if ( pre[u]==0 )//若前面没有小道
x = square( small_rode[u] + G.arcs[u][v].adj );
else
x = big_road[pre[u]] + square( small_rode[u]+G.arcs[u][v].adj );
}
if ( big_road[u]!=INFINITY)//如果这个路口和1号路口有大道相连
y = square( G.arcs[u][v].adj ) + big_road[u];
if ( x<y )
{
small_rode[v] = min( small_rode[v], small_rode[u]+G.arcs[u][v].adj );
square_road[v] = min( x, square_road[v] );//存储之前遍历的小道路径的和的平方
}
else
{
small_rode[v] = min( small_rode[v], G.arcs[u][v].adj);
square_road[v] = min( y, square_road[v] );
pre[v] = u;
}
pass_road[v] = min( square_road[v] , big_road[v] );
}
else//当前道路是大道
{
if ( big_road[u]!=INFINITY )
x = big_road[u] + G.arcs[u][v].adj;
if ( small_rode[u]!=INFINITY )
y = square_road[u] + G.arcs[u][v].adj;
long long mini = min(x,y);
big_road[v] = min( big_road[v], mini );
pass_road[v] = min( square_road[v], big_road[v] );
}
}
}
}
return;
}
int main()
{
int i;
MGraph G;
CreateGraph(G);
ShortestPath_DIJ( 1, G );
cout<< pass_road[G.vexnum]<<endl;
cout<< "最优路劲:";
for ( i=0; i<G.vexnum; i++ )
cout<< road[i]<<" ";
cout<<endl<< "输出出发点到各个点的疲劳值:";
for ( i=1; i<=G.vexnum; i++ )
cout<< pass_road[i]<<" ";
return 0;
}