利用图论中典型算法,实现下述功能:
1.创建景区景点图
2.查询景点信息
3.旅游景点导航(DFS 深度优先搜索)
4.搜索最短路径(迪杰斯特拉算法)
5.铺设电路规划(prim/kruscal算法)
main.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include"Tourism.h"
#include"Graph.h"
using namespace std;
int main(void)
{
int choice;
while (true)
{
cout << "==== 景区管理系统 ====" << endl;
cout << "1.创建景区景点图" << endl;
cout << "2.查询景点信息" << endl;
cout << "3.旅游景点导航" << endl;
cout << "4.搜索最短路径" << endl;
cout << "5.铺设电路规划" << endl;
cout << "0.退出" << endl;
cout << "请输入操作编号(0~5):" << endl;
cin >> choice;
switch (choice)
{
case 1:
system("cls");
CreateGraph();
system("pause");
break;
case 2:
system("cls");
GetSpotInfo();
system("pause");
break;
case 3:
system("cls");
TravelPath();
system("pause");
break;
case 4:
system("cls");
FindShortPath();
system("pause");
break;
case 5:
system("cls");
DesignPath();
system("pause");
break;
case 0:
cout << "退出系统" << endl;
exit(0);
break;
default:
cout << "输入错误编号 默认退出系统" << endl;
exit(0);
break;
}
}
}
Graph.h
#ifndef GRAPH_H_INCLUDED
#define GRAPH_H_INCLUDED
struct Vex
{
int num;
char name[20];
char desc[1024];
};
struct Edge
{
int vex1;
int vex2;
int weight;
};
struct CCgraph
{
int m_aAdjMatrix[20][20];//邻接矩阵
Vex m_aVexs[20];//顶点信息 里面有景点编号、名字、描述
int m_nVexNum;//顶点个数
};//Graph对象,用于存储景区景点图
typedef struct Path
{
int cVex[20];//保存一条路径
Path *next;//保存下一条路径
}*PathList;;//*PathList是指向结构体的指针
void Init(void);
bool InsertVex(Vex sVex);
bool InsertEdge(Edge sEdge);
Vex GetVex(int nVex);
int FindEdge(int nVex, Edge aEdge[]);
void DFS(int nVex, bool bVisited[], int &nIndex, PathList &pList);
void DFSTraverse(int nVex, PathList &pList);
int FindShortPath(int nVexStart, int nVexEnd, Edge aPath[]);
int FindMinTree(int nStart, Edge aEdge[]);
#endif //GRAPH_H_INCLUDED
Graph.cpp
#define _CRT_SECURE_NO_WARNINGS//消除scanf警告
#include"Graph.h"
#include"Tourism.h"
#include<cstring>
#include<iostream>
#define INF 32767;
using namespace std;
CCgraph m_Graph;
//初始化图信息
void Init(void)
{
m_Graph.m_nVexNum = 0;
int i;
for (i = 0; i < 20; i++)
{
m_Graph.m_aVexs[i].num = -1;
strcpy(m_Graph.m_aVexs[i].name, "");
strcpy(m_Graph.m_aVexs[i].desc, "");
for (int j = 0; j < 20; j++)
m_Graph.m_aAdjMatrix[i][j] = 0;
}
}
//插入点
bool InsertVex(Vex sVex)
{
if (m_Graph.m_nVexNum == 32767)
return false;
m_Graph.m_aVexs[m_Graph.m_nVexNum++] = sVex;
return true;
}
//插入边
bool InsertEdge(Edge sEdge)
{
if (sEdge.vex1 < 0 || sEdge.vex1 >= m_Graph.m_nVexNum ||
sEdge.vex2 < 0 || sEdge.vex2 >= m_Graph.m_nVexNum)
return false;
m_Graph.m_aAdjMatrix[sEdge.vex1][sEdge.vex2] = sEdge.weight;
m_Graph.m_aAdjMatrix[sEdge.vex2][sEdge.vex1] = sEdge.weight;
return true;
}
Vex GetVex(int nVex){
return m_Graph.m_aVexs[nVex];
}
/*
寻找周围景区 相连边存放于aEdge数组中
周围景区数目
*/
int FindEdge(int nVex, Edge aEdge[]) {
int k = 0;
for (int i = 0; i < m_Graph.m_nVexNum; i++)
{
if (m_Graph.m_aAdjMatrix[nVex][i] != 0)
{
aEdge[k].vex1 = nVex;
aEdge[k].vex2 = i;
aEdge[k].weight = m_Graph.m_aAdjMatrix[nVex][i];
k++;
}
}
return k; //返回周围景点的数目
}
void DFSTraverse(int nVex, PathList &pList) {
bool bVisited[32767] = { false };
int nIndex = 0;
DFS(nVex, bVisited, nIndex, pList);
}
//顶点编号 记录某个顶点是否被遍历过 遍历的深度 遍历得到的结果
void DFS(int nVex, bool bVisited[], int &nIndex, PathList &pList) {
bVisited[nVex] = true; //改为已访问
pList->cVex[nIndex++] = nVex; //访问顶点nVex
int nVexNum = 0;
for (int i = 0; i < m_Graph.m_nVexNum; i++) {
if (bVisited[i]) nVexNum++;
}
//判断是否所有顶点都访问过
if (nVexNum == m_Graph.m_nVexNum) {
pList->next = (PathList)malloc(sizeof(Path));
for (int i = 0; i < m_Graph.m_nVexNum; i++){
pList->next->cVex[i] = pList->cVex[i];
}
pList = pList->next;
pList->next = NULL;//链表移动
}
else{
for (int i = 0; i < m_Graph.m_nVexNum; i++)
{
if (!bVisited[i]&&(m_Graph.m_aAdjMatrix[nVex][i]>0)&& (m_Graph.m_aAdjMatrix[nVex][i]!=32767)){
//递归调用得到一条路径
DFS(i, bVisited, nIndex, pList);
bVisited[i] = false; //得到一条路径后将访问的i点置为false
nIndex--;//回溯
}
}
}
}
//功能4.搜索最短路径 Dijkstra贪心算法 贪心策略、最优子结构
int FindShortPath(int nVexStart, int nVexEnd, Edge aPath[]) {
//初始化最短路径
int flag[20],pre[20]; //标记数组,前置顶点
int dist[20],k;
for (int i = 0; i < m_Graph.m_nVexNum; i++){
flag[i] = 0; //默认都没置入集合
pre[i] = -1;
if (m_Graph.m_aAdjMatrix[nVexStart][i] > 0||i == nVexStart) {
dist[i] = m_Graph.m_aAdjMatrix[nVexStart][i];//两点距离
pre[i] = nVexStart; //当两个边直接相连时,pre初始化为起点
}
else{
dist[i] = INF;//两点距离
}
}
flag[nVexStart] = 1; //第一个并入的应该是nStartVex点
int min;
//遍历 m_Graph.m_nVexNum-1次:每次找出一个顶点的最短路径
for (int i = 1; i < m_Graph.m_nVexNum; i++) {
//寻找当前最小的路径
min = INF;
for (int j = 0; j < m_Graph.m_nVexNum; j++){
if (flag[j]==0 && dist[j]<min){
min = dist[j];
k = j; //k是已经获得最短路径的那个点
}
}
flag[k] = 1;
if (k == nVexEnd) { //k为终点,返回边的数目
break;
}
//将k作为中间点计算nVexSart到所有顶点的最短路径,进行扩充集合
for (int j = 0; j < m_Graph.m_nVexNum; j++) {
int tmp;
if (m_Graph.m_aAdjMatrix[k][j] == 0){
tmp = INF;
}
else{
tmp= min + m_Graph.m_aAdjMatrix[k][j];
}
if (flag[j]==0 && (tmp<dist[j])){
dist[j] = tmp;
pre[j] = k;
}
}
}
//cout << dist[nVexEnd] << endl;
int Num = 0; //总共Num条路径
int i = nVexEnd;//从终点开始回溯
while (i != nVexStart){
aPath[Num].vex2 = i;
aPath[Num].vex1 = pre[i];
aPath[Num].weight = m_Graph.m_aAdjMatrix[pre[i]][i];
i = pre[i];
Num++;
}
//cout << Num << endl;
return Num;
}
//功能5.铺设电路规划 Prim贪心算法 贪心策略 最优子结构
int FindMinTree(int nStart,Edge aEdge[]) {
int flag[20],closest[20];//标记数组 前置顶点
int lowcost[20];//最近顶点
for (int i = 0; i < m_Graph.m_nVexNum; i++) { //flag、closest、lowcost集合初始化
closest[i] = -1;
flag[i] = 0 ;
if (m_Graph.m_aAdjMatrix[nStart][i] > 0 || i == nStart) {
lowcost[i] = m_Graph.m_aAdjMatrix[nStart][i];
closest[i] = nStart;
}
else{
lowcost[i] = INF;
}
}
flag[nStart] = 1;
int Num = 0;
//找出m_Graph.m_nVexNum-1条边
int min,k;
for (int i = 1; i < m_Graph.m_nVexNum; i++) {
min = INF;
for (int j = 0; j < m_Graph.m_nVexNum; j++){
if (flag[j]==0&&lowcost[j]<min){
min = lowcost[j];
k = j;
}
}
flag[k] = 1;
for (int j = 0; j <m_Graph.m_nVexNum ; j++){
if (flag[j] == 0 && m_Graph.m_aAdjMatrix[k][j] < lowcost[j]&&m_Graph.m_aAdjMatrix[k][j]!=0) {
lowcost[j] = m_Graph.m_aAdjMatrix[k][j];
closest[j] = k;
}
}
//存储各边信息至aEdge数组中
flag[closest[k]] = 1;
aEdge[Num].vex1 = closest[k];
aEdge[Num].vex2 = k;
aEdge[Num].weight = m_Graph.m_aAdjMatrix[closest[k]][k];
Num++;
}
return Num;//返回边的数目
}
Tourism.h
#ifndef TOURISM_H_INCLUDED
#define TOURISM_H_INCLUDED
void CreateGraph(void);
void GetSpotInfo(void);
void TravelPath();
void FindShortPath(void);
void DesignPath(void);
#endif // TOURISM_H_INCLUDED
Toutism.cpp
#define _CRT_SECURE_NO_WARNINGS//消除scanf警告
#include"Tourism.h"
#include"Graph.h"
#include<iostream>
#include<cstring>
using namespace std;
extern CCgraph m_Graph;
//功能1. 创建景区顶点
void CreateGraph(void){
Init();
cout << "===== 创建景区管理图 =====" << endl;
int num;
FILE *InVex = fopen("D:\\Vex.txt", "r");
fscanf(InVex, "%d", &num);
cout << "顶点数目:" << num << endl;
cout << "----- 顶点 -----" << endl;
for (int i = 0; i < num; i++)
{
Vex vex;
fscanf(InVex, "%d", &(vex.num));
fscanf(InVex, "%s", vex.name);
fscanf(InVex, "%s", vex.desc);
cout << vex.num << "-" << vex.name << endl;
InsertVex(vex);
}
fclose(InVex);
cout << "----- 边 -----" << endl;;
FILE *InEdge = fopen("D:\\Edge.txt","r");
while(!feof(InEdge))
{
Edge edge;
fscanf(InEdge, "%d %d %d", &edge.vex1, &edge.vex2, &edge.weight);
cout << "<v" << edge.vex1 << ",v" << edge.vex2 << "> " << edge.weight << endl;
InsertEdge(edge);
}
fclose(InEdge);
/*
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 7; j++) {
cout << m_Graph.m_aAdjMatrix[i][j]<<" ";
}
cout << endl;
}
*/
}
//功能2. 查询景点信息 直接查询
void GetSpotInfo(void)
{
if (m_Graph.m_nVexNum == 0) {
cout << "景区无信息 请首先创建景区信息" << endl;
return;
}
cout << "===== 查询景点信息 =====" << endl;
int i;
for (i = 0; i < m_Graph.m_nVexNum; i++)
cout << m_Graph.m_aVexs[i].num << "-" << m_Graph.m_aVexs[i].name << endl;
cout << "请输入想要查询的景点编号:";
int Num;
cin >> Num;
if (Num >= m_Graph.m_nVexNum) {
cout << "编号输入错误 默认退出" << endl;
return;
}
Vex vex = GetVex(Num);
cout << vex.desc << endl;
cout << "----- 周围景区 ------" << endl;
Edge aEdge[100];
int sum_Edge=FindEdge(Num, aEdge);
for (i = 0; i < sum_Edge; i++) {
if (aEdge[i].weight!=32767){
cout << m_Graph.m_aVexs[aEdge[i].vex1].name << "->" <<
m_Graph.m_aVexs[aEdge[i].vex2].name << " " << aEdge[i].weight << "m" << endl;
}
}
}
//功能3. 旅游景点导航 DFS算法改进
void TravelPath() {
if (m_Graph.m_nVexNum == 0) {
cout << "景区无信息 请首先创建景区信息" << endl;
return;
}
for (int i = 0; i < m_Graph.m_nVexNum; i++)
cout << m_Graph.m_aVexs[i].num << "-" << m_Graph.m_aVexs[i].name << endl;
cout << "请输入起始点编号: ";
int Num;
cin >> Num;
if (Num < 0 || Num >= m_Graph.m_nVexNum) {
cout << "编号输入错误 默认退出" << endl;
return;
}
cout << "导航路线为:" << endl;
PathList pList=(PathList)malloc(sizeof(Path));
PathList Head = pList;
//调用DFSTraverse函数
DFSTraverse(Num, pList);
pList = Head ;
pList = pList->next;
int i = 1;
while (pList!=NULL){
cout << "路线" << i << ":";
for (int i = 0; i < m_Graph.m_nVexNum; i++){
cout << m_Graph.m_aVexs[pList->cVex[i]].name << " ";
}
cout << endl;
pList = pList->next;
i++;
}
free(pList);
pList = NULL;
Head = NULL;
}
//功能4.搜索最短路径 Dijkstra贪心算法 贪心策略、最优子结构
//参考:https://blog.youkuaiyun.com/chen134225/article/details/79886928
void FindShortPath(void) {
if (m_Graph.m_nVexNum == 0) {
cout << "景区无信息 请首先创建景区信息" << endl;
return;
}
int nStart, nEnd;
cout << "请输入起点的编号:" << endl;
cin >> nStart;
cout << "请输入终点的编号:" << endl;
cin >> nEnd;
if (nStart > nEnd || nEnd >= m_Graph.m_nVexNum) {
cout << "编号输入错误 默认退出" << endl;
return;
}
if (nStart == nEnd) {
cout << "最短距离为:0" << endl;
return;
}
Edge aPath[32767];
int Num=FindShortPath(nStart, nEnd, aPath);
cout << "最短路径为:"<<endl;
int Path = 0;
for (int i = Num-1; i>=0; i--){
cout << m_Graph.m_aVexs[aPath[i].vex1].name <<"->";
cout << m_Graph.m_aVexs[aPath[i].vex2].name << endl;
Path += aPath[i].weight;
}
cout << "最短路径为:" << Path << endl;
}
//功能5.铺设电路规划 Prim贪心算法
void DesignPath(void){
if (m_Graph.m_nVexNum == 0) {
cout << "景区无信息 请首先创建景区信息" << endl;
return;
}
int n = m_Graph.m_nVexNum - 1;
cout<< "请输入您要铺设电路的起始顶点:输入(" <<0<<"/"<<n<<")"<< endl;
int nStart;
cin >> nStart;
if (nStart!=0&&nStart!=n) {
cout << "编号输入错误 默认退出" << endl;
return;
}
Edge aEdge[20];
int Num = FindMinTree(nStart, aEdge),Path=0;
//输出
cout << "在以下两个景点之间铺设电路:" << endl;
for (int i = 0; i < Num; i++){
cout << m_Graph.m_aVexs[aEdge[i].vex1].name << " - " << m_Graph.m_aVexs[aEdge[i].vex2].name << " "
<< aEdge[i].weight << "m" << endl;
Path += aEdge[i].weight;
}
cout << "铺设电路的总长度为:" << Path <<"m"<< endl;
}
前几天抽时间肝完的,整个实验难度不大,书上都给出了各个文件函数调用顺序,写的时候主要是一些小bug。
自己算法能力还差很多,以后尤其要注意这些前人已发明的算法使用,牢记于心,在一些算法题中通过这些算法帮助快捷地解决问题。
加油…还有连连看。。