目录
问题描述
题目内容
上网下载真实南京公交线路图,建立南京主要公交线路图的存储结构。
基本要求
- 输入任意两站点,给出转车次数最少的乘车路线。
- 输入任意两站点,给出经过站点最少的乘车路线。
问题分析
需求分析
需要将公交路线文件进行处理,获取每一个站点与公交的信息,最后进行两种路线的查询。
数据结构
本题采用数据结构:图
采用该结构的原因:由于节点之间有路径,每个节点有编号,且搜索相关路径,需使用图来解决该题。
算法设计
由于两种路线都与求最短相关路径有关,故都采用迪杰斯特拉算法。
求经过站点最少的路径:设置三个辅助数组,dist[]、path[]和visited[].其中dist[i]表示起始站点到站点i的最短距离;若path[i]=j,表示从起始站点到达站点i会经过站点j;visited [i]表示顶点i的选中状态,若被选中,则下一次访问不考虑该节点。由于最少站点路径只关乎站点个数,所以每个站点距离其可到达的站点距离记作1。初始情况下,只有起始点被选中,其余顶点均是未选中状态。dist数组中记录的是起始点与其余顶点之间当前的最短路径,即起始点与其余顶点边的权值,如果不存在边,则权值为无穷大。循环遍历dist数组,每次遍历都选出dist数组中最小元素值并进行标记,随后更新其余未标记点的状态,直至能到达的顶点都被标记。
求换乘次数最少的路径:设置五个辅助数组,dist[]、path[]、visited[]、changebus[]、pathbus[][],其中dist[i] 表示起始站点到站点i的最少换乘次数;若path[i]=j,表示从起始站点到达站点i会经过站点j;visited [i]表示顶点i的选中状态,若被选中,则下一次访问不考虑该节点;changebus[i]表示与path[i]=j相关的j节点到i节点是否需要换乘;pathbus[i][]表示与path[i]=j相关的j节点到i节点乘坐的公交车。若不需换乘dist[i]=dist[j],如需要dist[i]=dist[j]+1,其余思路与第一问相似。
源代码
Bus.h
#include <iostream>
#include <fstream>
#include <stack>
using namespace std;
#define Msize 950
#define Maxnum 0x3f3f3f3f
struct Node
{
char name[100];
int bus_num;
int value;
Node* next;
};//邻接表节点
struct Hnode
{
char name[100];
Node* first;
};//邻接表头节点
struct Graph
{
Hnode H[Msize];
int sum;
bool M[Msize][Msize];//邻接矩阵
};//表节点
char line[1005];//文件操作辅助字符串
int dist[Msize];//起始点与其余顶点之间当前的最短路径
int path[Msize];//起始站点到达站点i会经过的站点
int ans[Msize];
bool visited[Msize];//表示顶点的选中状态
bool changebus[Msize];//表示顶点的换乘状态
int pathbus[Msize][20];//表示到达顶点的公交车
stack<int> s1;
stack<int> s2;
/**相同字符串比较函数**/
bool Str_Cmp(char* key_1, char* key_2)
{
if (key_1 == key_2)
{
return true;
}//同一个字符串
int start = 0;
while (key_1[start] != '\0' && key_2[start] != '\0')
{
if (key_1[start] != key_2[start])
{
break;
}
start++;
}//依次比较
if (key_1[start] == '\0' && key_2[start] == '\0')
{
return true;
}
else
{
return false;
}
}
/**站点分割函数**/
bool Divid_Station(char key_1)
{
if (key_1 == ',' || key_1 == '\0')
{
return true;
}//遇到逗号产生一个站点
return false;
}
/**字符串移植函数**/
void Str_Cpy(char* key_1, char* key_2)
{
int start = 0;
while (key_1[start] != '\0')
{
key_2[start] = key_1[start];
start++;
}//将key_1的内容放入key_2中
key_2[start] = '\0';
}
/*处理站点*/
void Get_Information_From_File(char** s, int& k, Graph& G)
{
//将每个站点分开放入station.txt中
fstream bfile, sfile, csfile;
bfile.open("JiangNing.txt", ios::in);
sfile.open("station.txt", ios::out);
if (!bfile.good() || !sfile.good())
{
cout << "文件打开失败" << endl;
exit(1);
}
while (!bfile.eof() && bfile.peek() != EOF)
{
char add_station;
bfile.getline(line, 1000);
int start = 8;
while (line[start] != '\0' && line[start] != '\n')
{
int temp_start = 0;
char temp_char[100];
for (int i = 0; i < 100; i++)
{
temp_char[i] = '\0';
}
while (line[start] != '\0' && line[start] != '\n' && line[start] != ' ')
{
add_station = line[start];
if (!Divid_Station(add_station))
{
temp_char[temp_start] = line[start];
temp_start++;
start++;
}
else
{
start++;
break;
}
}
sfile << temp_char << endl;
}
}
bfile.close();
sfile.close();
//将重复站点删除
csfile.open("Fstation.txt", ios::out);
sfile.open("station.txt", ios::in);
if (!csfile.good() || !sfile.good())
{
cout << "文件打开失败" << endl;
exit(1);
}
char ch[100];
while (!sfile.eof() && sfile.peek() != EOF)
{
int i = 0;
sfile.getline(ch, 95);
for (i = 0; i < k; i++)
{
if (Str_Cmp(ch, s[i]))
{
break;
}
}
if (i == k)
{
s[k] = (char*)malloc(sizeof(char) * 100);
if (!s[k])
{
exit(1);
}
Str_Cpy(ch, s[k]);
k++;
csfile << ch << endl;
}
}
csfile.close();
sfile.close();
fstream tfile;
//将中文站点换成编号
bfile.open("JiangNing.txt", ios::in);
tfile.open("Bus_NUm.txt", ios::out);
if (!bfile.good() || !tfile.good())
{
cout << "文件打开失败" << endl;
exit(1);
}
while (!bfile.eof() && bfile.peek() != EOF)
{
char add_station;
bfile.getline(line, 1000);
tfile << line[0] << line[1] << line[2] << ' ';
int start = 8;
while (line[start] != '\0')
{
int temp_start = 0;
char temp_char[100];
for (int i = 0; i < 100; i++)
{
temp_char[i] = '\0';
}
while (line[start] != '\0' && line[start] != '\n')
{
add_station = line[start];
if (!Divid_Station(add_station))
{
temp_char[temp_start] = line[start];
temp_start++;
start++;
}
else
{
start++;
break;
}
}
for (int j = 0; j < k; j++)
{
if (Str_Cmp(s[j], temp_char))
{
tfile << j + 1 << ' ';
break;
}
}
}
tfile << endl;
}
bfile.close();
tfile.close();
//将每个站点可到达的站点存储
fstream ctfile;
tfile.open("Bus_Num.txt", ios::in);
ctfile.open("Graph.txt", ios::out);
if (!tfile.good() || !ctfile.good())
{
cout << "Bus_Num.txt或Graph.txt无法打开" << endl;
exit(1);
}
for (int i = 1; i <= G.sum; i++)
{
while (!tfile.eof() || tfile.peek() != EOF)
{
tfile.getline(line, 1000);
int start = 4;
while (line[start] != '\0' && line[start] != '\n')
{
int temp_num = 0;
while (line[start] != ' ' && line[start] != '\0' && line[start] != '\n')
{
temp_num = temp_num * 10 + (line[start] - ('0' - 0));
start++;
}
start++;
if (temp_num == i)
{
int temp_next = 0;
if (line[start] != ' ' && line[start] != '\0' && line[start] != '\n')
{
while (line[start] != ' ' && line[start] != '\0' && line[start] != '\n')
{
temp_next = temp_next * 10 + (line[start] - ('0' - 0));
start++;
}
ctfile << temp_next * 1000 + (line[0] - ('0' - 0)) * 100 + (line[1] - ('0' - 0)) * 10 + (line[2] - ('0' - 0)) << ' ';
}
}
}
}
ctfile << endl;
tfile.clear();
tfile.seekg(0L, ios::beg);
}
tfile.close();
ctfile.close();
}
/**创建图**/
void Creat_Graph(Graph& G, char** s)
{
for (int i = 1; i <= G.sum; i++)
{
G.H[i].first = NULL;
}//初始化
fstream cfile;
cfile.open("Graph.txt", ios::in);
if (!cfile.good())
{
exit(1);
}
int under = 1;
//从文件中获取信息构建图
while (cfile.peek() != EOF && !cfile.eof())
{
Str_Cpy(s[under - 1], G.H[under].name);
cfile.getline(line, 1000);
int start = 0;
while (line[start] != '\n' && line[start] != '\0')
{
int temp = 0;
while (line[start] != '\n' && line[start] != '\0' && line[start] != ' ')
{
temp = temp * 10 + (line[start] - ('0' - 0));
start++;
}
start++;
int temp_s, temp_h;
temp_s = temp / 1000;
temp_h = temp % 1000;
Node* p = (Node*)malloc(sizeof(Node));
if (!p)
{
exit(1);
}
Str_Cpy(s[temp_s - 1], p->name);
p->bus_num = temp_h;
p->next = G.H[under].first;
G.H[under].first = p;
G.M[under][temp_s] = 1;//邻接矩阵构建
}//邻接表的构建
under++;
}
cfile.close();
}
/**文件中找站对应编号**/
void Check_Station(Graph& G, char* b, char* e, int& bs, int& es, char** s)
{
//从s数组中找符合的站点名称
for (int i = 0; i < G.sum; i++)
{
if (Str_Cmp(b, s[i]))
{
bs = i + 1;
}
if (Str_Cmp(e, s[i]))
{
es = i + 1;
}
if (bs && es)
{
break;
}
}
}
/**输出方案**/
void Print_Solution(char** s, Graph& G)
{
int top, ntop;
int temp_bus = 0;
int i = 0;
//将s1栈中存储的站点输出
while (!s1.empty())
{
top = s1.top();
i++;
s1.pop();
if (!s1.empty())
{
ntop = s1.top();
Node* p = G.H[top].first;
while (p)//遍历找到合适的公交
{
if (Str_Cmp(s[ntop - 1], p->name))
{
if (temp_bus != p->bus_num)
{
cout << endl;
cout << " (乘" << p->bus_num << "路)" << endl;
cout << " " << s[top - 1] << " -> " << s[ntop - 1];
temp_bus = p->bus_num;
}
else
{
cout << " -> " << s[ntop - 1] << ' ';
}
break;
}
p = p->next;
}
}
}
cout << endl;
cout << endl;
cout << " *共经过" << i << "站*" << endl;
cout << endl;
cout << endl;
}
/**迪杰斯特拉**/
void Dijkstra(Graph& G, int start, int es, char** s)
{
for (int i = 1; i <= G.sum; i++)
{
if (G.M[start][i])
{
dist[i] = 0;
path[i] = start;
}
else
{
dist[i] = Maxnum;
path[i] = -1;
}
visited[i] = 0;
}//初始化辅助数组
visited[start] = 1;//将起始点设置为被选中状态
int minunder = 0;
for (int j = 1; j < G.sum; j++)//循环选中剩余的顶点
{
int mindist = Maxnum;
for (int i = 1; i <= G.sum; i++)
{
if (dist[i] < mindist && !visited[i])
{
mindist = dist[i];
minunder = i;
}
}//在 dist数组中寻找visited标志为false的元素的最小值
if (dist[minunder] < Maxnum)
{
visited[minunder] = 1;//找到后将其设置为选中状态
for (int i = 1; i <= G.sum; i++)
{
if (!visited[i] && G.M[minunder][i] && dist[i] > dist[minunder] + 1)//顶点j经过k到源点比原来更近
{
path[i] = minunder;
dist[i] = dist[minunder] + 1;
}
}
}
}
if (visited[es] == 1)
{
int i = es;
s1.push(es);
while (path[i] != start)
{
s1.push(path[i]);
i = path[i];
}
s1.push(start);
cout << endl;
cout << "***经过最少站点的方案***" << endl;
Print_Solution(s, G);
}
else
{
cout << "暂时未找到合适方案" << endl;
}
}
/**选择正确公交车**/
bool Judge(int i, char* bs, char* es, char** s)
{
//遍历文件,判断公交车是否符合要求
int flag1 = 0, flag2 = 0;
fstream bfile;
bfile.open("Bus_Num.txt", ios::in);
if (!bfile.good())
{
exit(1);
}
while (bfile.peek() != EOF && !bfile.eof() && !flag1 && !flag2)
{
bfile.getline(line, 1000);
int temp = 0;
temp = (line[0] - ('0' - 0)) * 100 + (line[1] - ('0' - 0)) * 10 + (line[2] - ('0' - 0));
if (temp == i)
{
int start = 4;
while (line[start] != '\n' && line[start] != '\0')
{
int x = 0;
while (line[start] != '\n' && line[start] != '\0' && line[start] != ' ')
{
x = x * 10 + (line[start] - ('0' - 0));
start++;
}
start++;
if (Str_Cmp(bs, s[x - 1]))
{
flag1 = 1;
}
if (Str_Cmp(es, s[x - 1]))
{
flag2 = 1;
}
}
break;
}
}
bfile.close();
if (flag1 && flag2)
{
return 1;
}
return 0;
}
void Dijkstra_2(Graph& G, int start, int es, char** s)
{
int times = 0;
cout << "***换乘最少的方案如下***" << endl;
for (int i = 1; i <= G.sum; i++)
{
if (G.M[start][i])
{
dist[i] = 0;
path[i] = start;
Node* p = G.H[start].first;
while (p)
{
if (Str_Cmp(p->name, s[i - 1]))
{
int t = pathbus[i][0];
pathbus[i][t + 1] = p->bus_num;
pathbus[i][0]++;
}
p = p->next;
}
}
else
{
dist[i] = Maxnum;
path[i] = -1;
}
visited[i] = 0;
}//初始化辅助数组
visited[start] = 1;//将起始点设置为被选中状态
int minunder = 0;
for (int j = 1; j < G.sum; j++)
{
int mindist = Maxnum;
for (int i = 1; i <= G.sum; i++)
{
if (dist[i] < mindist && !visited[i])
{
mindist = dist[i];
minunder = i;
}
}//在 dist数组中寻找visited标志为false的元素的最小值
if (dist[minunder] < Maxnum)
{
visited[minunder] = 1;
for (int i = 1; i <= G.sum; i++)//对整体进行依次改动
{
if (!visited[i] && G.M[minunder][i] && dist[i] > dist[minunder])//如果可以对该点进行改动
{
pathbus[i][0] = 0;
int change = 1;//是否需要换乘的标志
Node* p = G.H[minunder].first;
while (p)
{
if (Str_Cmp(s[i - 1], p->name))//选出该点获取信息
{
for (int t = 1; t <= pathbus[minunder][0]; t++)
{
if (pathbus[minunder][t] == p->bus_num)
{
change = 0;//不需要换乘
changebus[i] = 0;
path[i] = minunder;
int start = pathbus[i][0];
pathbus[i][start + 1] = p->bus_num;//将不需要换乘乘坐的公交号存入
dist[i] = dist[minunder];
pathbus[i][0]++;
}
}
}
p = p->next;
}
if (change)//虽然可以到达,还是需要换乘
{
changebus[i] = 1;
p = G.H[minunder].first;
while (p)
{
if (Str_Cmp(s[i - 1], p->name))//选出该点获取信息
{
int start = pathbus[i][0];
pathbus[i][start + 1] = p->bus_num;//将不需要换乘乘坐的公交号存入
dist[i] = dist[minunder] + 1;
pathbus[i][0]++;
path[i] = minunder;
}
p = p->next;
}
}
}
}
}
}
int anstart = 1;
if (visited[es] == 1)
{
int i = es;
s2.push(es);
while (path[i] != start)
{
s2.push(path[i]);
i = path[i];
}
s2.push(start);
while (!s2.empty())
{
ans[anstart++] = s2.top();
s2.pop();
}
anstart--;
for (int k = 1; k <= anstart; k++)
{
int sk = k;
int nk = k;
if (!changebus[ans[nk]])//起始点
{
while (!changebus[ans[nk]] && nk <= anstart)
{
nk++;
}
nk--;
Node* x = G.H[ans[sk]].first;
while (x)//遍历
{
if (Judge(x->bus_num, s[ans[sk] - 1], s[ans[nk] - 1], s))
{
times++;
cout << " (乘" << x->bus_num << "路)" << endl;
while (sk <= nk)
{
cout << s[ans[sk] - 1] << " -> ";
sk++;
}
cout << "下车";
cout << endl;
break;
}
x = x->next;
}
}
else//非起始点
{
sk--;
nk++;
while (!changebus[ans[nk]] && nk <= anstart)
{
nk++;
}
nk--;
Node* x = G.H[ans[sk]].first;
while (x)//遍历
{
if (Judge(x->bus_num, s[ans[sk] - 1], s[ans[nk] - 1], s))
{
times++;
cout << " (乘 " << x->bus_num << "路)" << endl;
while (sk <= nk)
{
cout << s[ans[sk] - 1] << " -> ";
sk++;
}
cout << "下车";
cout << endl;
break;
}
x = x->next;
}
}
k = nk;
}
cout << endl;
cout << " *共换乘" << times - 1 << "次" << endl;
}
else
{
cout << "暂时未找到合适方案" << endl;
}
}
Main.cpp
#include <iostream>
#include "Bus.h"
using namespace std;
char* s[2000];
int k = 0;
int bs = 0, es = 0;
char bstion[100], estion[100];
Graph G;
Graph G2;
int main()
{
G.sum = 929;
Get_Information_From_File(s, k, G);
Creat_Graph(G, s);
cout << "输入起始站:";
cin >> bstion;
cout << "输入终点站:";
cin >> estion;
cout << endl;
Check_Station(G, bstion, estion, bs, es, s);
Dijkstra(G, bs, es, s);
Dijkstra_2(G, bs, es, s);
return 0;
}