公园导游系统(求任意两点最短路径,Floyd算法,DFS)

本程序是本人大二课程设计自己写滴,希望对您有帮助!!

课题设计简要任务:

  1. 构建公园无向图,领接矩阵或邻接表储存;
  2. 要求求任意两个景点的最短路径;
  3. 列出入口到出口的路径(路径条数有上限)。
  4. 要求用文本读入的方式读取数据

目录

一、景点储存与读入

二、DFS

三、Floyd

四、测试

五、代码


一、景点储存与读入

本人用的是邻接表的方式储存数据

struct Head_Node //头节点,纵向
{
	string name;
	struct Node* First; //指向的第一个Node 
	Head_Node()
	{
		name = "";
		First = NULL;
	}
};
struct Node //横向节点
{
	int index;//对应的下标(Head_Node中)
	struct Node* next;  
	double weight;//米
	Node()
	{
		index = 0;
		next = NULL;
		weight = int(1<<30);
	}
};

接下来是Park_guide类的建立

class Park_guide //有权无向图
{
private:
	vector<Head_Node> Net;//总网,纵向数组
	static int Max_Path;
	static int Path_Num;//路径数
	int Spot_Num;//景点数
	int Side_Num;//边数
	int Min_Path[Max_spot][Max_spot];//最短路径的导引数组,用于查任意两点最短路径
	double Map[Max_spot][Max_spot]; //对应的邻接矩阵
	//double Min_line[100][100];
public:
	Park_guide();//初始化地图
	~Park_guide();
	void Fp_in();//读入文件的函数
	int Find(vector<Head_Node> &x, string y);//返回景点y 在Net中的位置(纵向的坐标)
	void Inout_ways();//入口和出口的路径
	void Inout_Dfs(int temp, int In, int Out, int *arr, int *path);
	void Print_line(int In, int Out, int* path);//打印入口出口路线,最大 Max_Path 条
	double Weight(int x, int y);//返回以x - y为边的权值
	void Per_Min_line();
	void Tran_array();
	void Two_Mi_line(string de1, string de2);
	
	int Re_Num() { return Max_Path; }//返回最大路径数
};

上图可以方便理解

接下来是文件的读入

首先文件的读入有格式,如下:

n=7
入口 景点1 景点2 景点3 景点4 景点5 出口
v=10
入口 景点1 5
入口 景点3 10
景点1 景点3 8
景点1 景点2 7
景点2 景点5 7
景点5 景点3 6
景点5 景点4 4
景点4 出口 4
景点3 出口 20
景点2 景点4 5 

文件名为Test.txt;

n为景点数(包含入口和出口);

v为边数;

第二行要入口为第一个,出口为最后一个;

之间以空格间隔。

下面是函数实现

void Park_guide::Fp_in()
{
	ifstream ifs("Test.txt");//定义文件输入流
	(ifs).seekg(0);//定位到文件开头
	char ch=' ';
	string line="";//用于跳行
	while (1)
	{
		ifs.get(ch);
		if (ch == 'n') break;
		getline(ifs, line);
	}
	ifs.get(ch);//把等号忽略掉,从等号之后开始读
	int n=0;
	ifs >> n; //ifs可以与cin一样输入
	Spot_Num = n;
	//存景点
	for (int i = 0; i < n; ++i)
	{
		Head_Node* s = new Head_Node();
		ifs >> s->name;
		Net.push_back(*s);
	}
	ifs.get(ch);//把回车吃掉
	ch = ' ';
	line = ' ';
	while (1)
	{
		ifs.get(ch);
		if (ch == 'v') break;
		getline(ifs, line);
	}
	int v = 0;
	ifs.get(ch);
	ifs >> v;
	Side_Num = v;

	//初始化地图
	double w;
	string s1, s2;
	for (int i = 0; i < Side_Num; ++i)
	{
		Node* x = new Node();
		Node* y = new Node();
		//边s1,s2对应(x -w- y)
		ifs >> s1 >> s2 >> w;
		//x为始点,y为末点, 头插法
		y->index = Find(Net, s2);
		y->weight = w;
		y->next = Net[Find(Net, s1)].First;
		Net[Find(Net, s1)].First = y;
		//y为始点,x为末点
		x->index = Find(Net, s1);
		x->weight = w;
		x->next = Net[Find(Net, s2)].First;
		Net[Find(Net, s2)].First = x;
	}
}

二、DFS

用广搜邻接表来找入口与出口之间的路径,需要两个数组(这里用动态开辟了数组)来实现,arr数组用来标记当前是否走过,path数组用来记录路径便于输出

void Park_guide::Inout_ways()
{
	int s1, s2;
	s1 = Find(Net, Net[0].name);
	s2 = Find(Net, Net[Net.size()-1].name);
	int *arr = new int[Spot_Num];
	int *path = new int[Spot_Num];
	memset(arr, 0, Spot_Num*sizeof(int));//标记数组
	memset(path, -1, Spot_Num*sizeof(int));//路径
	Inout_Dfs(s1, s1, s2, arr, path);
	cout << endl;
	Path_Num = 0;
	delete []arr;
	delete []path;
}
void Park_guide::Inout_Dfs(int temp, int In, int Out, int *arr, int *path )
{
	if (Path_Num == Max_Path) return;//允许最大路径数
	if (temp == Out)//到了出口
	{
		Print_line(In, Out, path);
		Path_Num++;
		return;
	}
	arr[temp] = 1;
	Node* s = new Node();
	s = Net[temp].First;
	while (s != NULL)
	{
		//cout << s->index << endl;
		if (arr[s->index]==0)
		{
			path[temp] = s->index;
			Inout_Dfs(s->index, In, Out, arr, path);
			//回溯
			arr[s->index] = 0;
			path[temp] = -1;
		}
		s = s->next;
	}
}
void Park_guide::Print_line(int In, int Out, int* path)
{
	int i = In;
	int f = 1;
	double all = 0;
	if (path[i] == -1)
	{
		cout << "无路径"<<endl;
		return;
	}
	cout << "路线" <<Path_Num+1<< ":  ";
	while (path[i] != -1)
	{
		if (f)
		{
			cout << Net[i].name;
			all += Weight(i, path[i]);
			f = 0;
		}
		else
		{
			cout << " -> " << Net[i].name;
			all += Weight(i, path[i]);
		}
		i = path[i];
	}
	//出口节点
	cout << " -> " << Net[i].name;
	cout << endl << "总长度为:" << all << endl;
}

三、Floyd

用Floyd算法求任意两点的最短路径,需要两个辅助数组实现,Map[][]二维数组用来表示当前地图的邻接矩阵,后续用来表示两个节点之间的最短距离;Min_Path[][]二维数组用来查找最短路径(提前实现,后续用可以直接用)

Min_Path[i][j] 中的元素表示节点i到节点j的最短路径的第一步去的那个节点(初始化为j,即看成i与j有边),如此下去直到索引到节点j,最短路径就出来了

下面是实现

void Park_guide::Per_Min_line()
{
	Tran_array();//邻接表转邻接矩阵
	//Floyd算法。  以i为中间点,j到k的路径
	for (int i = 0; i < Spot_Num; ++i)
	{
		for (int j = 0; j < Spot_Num; ++j)
		{
			for (int k = 0; k < Spot_Num; ++k)
			{
				//发现j到i到k的路比j到k的路还要小,更新路径
				if (Map[j][i] + Map[i][k] < Map[j][k])
				{
					Map[j][k] = Map[j][i] + Map[i][k];
					//更新标记点,即j到k的路变为j到i,i到k的不用管
					Min_Path[j][k] = Min_Path[j][i];
				}
			}
		}
	}
}
void Park_guide::Tran_array()
{
	for (int i = 0; i < Spot_Num; ++i)
	{
		for (int j = 0; j < Spot_Num; ++j)
		{
			Min_Path[i][j] = j;
			if (i == j) Map[i][j] = 0;
			else Map[i][j] = int(1<<30);
		}
		if (Net[i].First)
		{
			Node* s = new Node();
			s = Net[i].First;
			while (s)
			{
				Map[i][s->index] = s->weight;
				s = s->next;
			}
		}
	}
}

需要三重循环来实现,有点想动态规划

重点是条件:Map[j][i] + Map[i][k] < Map[j][k] 成立时,即节点j经过节点i再到节点k,比节点j到节点k还要短,就要更新节点,更新两个辅助数组

四、测试

本人自拟的测试图

测试

五、代码

本程序有两部分

1.txt文本文件

把它放进编译文件同目录就可以,注意:文件名格式为"Test.txt"!!!!!!

本人用的时devc++,如图在目录创建一个记事本"Test.txt"文件,然后保存下面的景点数据(也可以自己设计一个地图)

n=7
入口 景点1 景点2 景点3 景点4 景点5 出口
v=10
入口 景点1 5
入口 景点3 10
景点1 景点3 8
景点1 景点2 7
景点2 景点5 7
景点5 景点3 6
景点5 景点4 4
景点4 出口 4
景点3 出口 20
景点2 景点4 5

2.完整代码,嘿嘿

#include<iostream>
#include<vector>
#include<string>
#include<fstream>
#include<string.h>
using namespace std;
#define Max_spot 100 //允许公园最大景点数
struct Head_Node //头节点,纵向
{
	string name;
	struct Node* First; //指向的第一个Node 
	Head_Node()
	{
		name = "";
		First = NULL;
	}
};
struct Node //横向节点
{
	int index;//对应的下标(Head_Node中)
	struct Node* next;  
	double weight;//米
	Node()
	{
		index = 0;
		next = NULL;
		weight = int(1<<30);
	}
};
class Park_guide //有权无向图
{
private:
	vector<Head_Node> Net;//总网,纵向数组
	static int Max_Path;
	static int Path_Num;//路径数
	int Spot_Num;//景点数
	int Side_Num;//边数
	int Min_Path[Max_spot][Max_spot];//最短路径的导引数组,用于查任意两点最短路径
	double Map[Max_spot][Max_spot]; //对应的邻接矩阵
	//double Min_line[100][100];
public:
	Park_guide();//初始化地图
	~Park_guide();
	void Fp_in();//读入文件的函数
	int Find(vector<Head_Node> &x, string y);//返回景点y 在Net中的位置(纵向的坐标)
	void Inout_ways();//入口和出口的路径
	void Inout_Dfs(int temp, int In, int Out, int *arr, int *path);
	void Print_line(int In, int Out, int* path);//打印入口出口路线,最大 Max_Path 条
	double Weight(int x, int y);//返回以x - y为边的权值
	void Per_Min_line();
	void Tran_array();
	void Two_Mi_line(string de1, string de2);
	
	int Re_Num() { return Max_Path; }//返回最大路径数
};

//实现
 
int Park_guide:: Path_Num = 0;
int Park_guide::Max_Path = 5;
Park_guide::Park_guide()
{
	Fp_in();
	Per_Min_line();//先构建最短路径图,后面要用直接用
}
Park_guide::~Park_guide()
{

}
int Park_guide::Find(vector<Head_Node> &x, string y)
{
	for (unsigned int i = 0; i < x.size(); ++i)
	{
		if (x[i].name == y) return i;
	}
	return -1;
}
void Park_guide::Inout_ways()
{
	int s1, s2;
	s1 = Find(Net, Net[0].name);
	s2 = Find(Net, Net[Net.size()-1].name);
	int *arr = new int[Spot_Num];
	int *path = new int[Spot_Num];
	memset(arr, 0, Spot_Num*sizeof(int));//标记数组
	memset(path, -1, Spot_Num*sizeof(int));//路径
	Inout_Dfs(s1, s1, s2, arr, path);
	cout << endl;
	Path_Num = 0;
	delete []arr;
	delete []path;
}
void Park_guide::Inout_Dfs(int temp, int In, int Out, int *arr, int *path )
{
	if (Path_Num == Max_Path) return;//允许最大路径数
	if (temp == Out)//到了出口
	{
		Print_line(In, Out, path);
		Path_Num++;
		return;
	}
	arr[temp] = 1;
	Node* s = new Node();
	s = Net[temp].First;
	while (s != NULL)
	{
		//cout << s->index << endl;
		if (arr[s->index]==0)
		{
			path[temp] = s->index;
			Inout_Dfs(s->index, In, Out, arr, path);
			//回溯
			arr[s->index] = 0;
			path[temp] = -1;
		}
		s = s->next;
	}
}
void Park_guide::Print_line(int In, int Out, int* path)
{
	int i = In;
	int f = 1;
	double all = 0;
	if (path[i] == -1)
	{
		cout << "无路径"<<endl;
		return;
	}
	cout << "路线" <<Path_Num+1<< ":  ";
	while (path[i] != -1)
	{
		if (f)
		{
			cout << Net[i].name;
			all += Weight(i, path[i]);
			f = 0;
		}
		else
		{
			cout << " -> " << Net[i].name;
			all += Weight(i, path[i]);
		}
		i = path[i];
	}
	//出口节点
	cout << " -> " << Net[i].name;
	cout << endl << "总长度为:" << all << endl;
}
double Park_guide::Weight(int x, int y)
{
	Node* s = new Node();
	s = Net[x].First;
	double wei=0;
	while (s != NULL)
	{
		if (s->index == y)
		{
			wei = s->weight;
		}
		s = s->next;
	}
	return wei;
}
void Park_guide::Per_Min_line()
{
	Tran_array();//邻接表转邻接矩阵
	//Floyd算法。  以i为中间点,j到k的路径
	for (int i = 0; i < Spot_Num; ++i)
	{
		for (int j = 0; j < Spot_Num; ++j)
		{
			for (int k = 0; k < Spot_Num; ++k)
			{
				//发现j到i到k的路比j到k的路还要小,更新路径
				if (Map[j][i] + Map[i][k] < Map[j][k])
				{
					Map[j][k] = Map[j][i] + Map[i][k];
					//更新标记点,即j到k的路变为j到i,i到k的不用管
					Min_Path[j][k] = Min_Path[j][i];
				}
			}
		}
	}
}
void Park_guide::Tran_array()
{
	for (int i = 0; i < Spot_Num; ++i)
	{
		for (int j = 0; j < Spot_Num; ++j)
		{
			Min_Path[i][j] = j;
			if (i == j) Map[i][j] = 0;
			else Map[i][j] = int(1<<30);
		}
		if (Net[i].First)
		{
			Node* s = new Node();
			s = Net[i].First;
			while (s)
			{
				Map[i][s->index] = s->weight;
				s = s->next;
			}
		}
	}
}
void Park_guide::Two_Mi_line(string de1, string de2)
{
	int x = Find(Net, de1);
	int y = Find(Net, de2);
	int t = x;
	//输入有问题的情况
	if (x == -1 || y == -1)
	{
		cout << "请输入正确的景点名称!" << endl<<"重新输入序号!"<<endl<<endl;
		return;
	}
	//自己到达自己的情况
	if (x == y)
	{
		cout << "已经到达景点" << endl;
		return;
	}
	
	cout << "最短游览路线:" << endl;
	cout << Net[x].name;
	while (Min_Path[x][y] != y)
	{
		cout <<" -> "<< Net[Min_Path[x][y]].name;
		x = Min_Path[x][y];
	}
	cout << " -> " << Net[Min_Path[x][y]].name << endl;
	cout << "总长度为:" << Map[t][y] <<"米"<< endl << endl;
}
void Park_guide::Fp_in()
{
	ifstream ifs("Test.txt");//定义文件输入流
	(ifs).seekg(0);//定位到文件开头
	char ch=' ';
	string line="";//用于跳行
	while (1)
	{
		ifs.get(ch);
		if (ch == 'n') break;
		getline(ifs, line);
	}
	ifs.get(ch);//把等号忽略掉,从等号之后开始读
	int n=0;
	ifs >> n; //ifs可以与cin一样输入
	Spot_Num = n;
	//存景点
	for (int i = 0; i < n; ++i)
	{
		Head_Node* s = new Head_Node();
		ifs >> s->name;
		Net.push_back(*s);
	}
	ifs.get(ch);//把回车吃掉
	ch = ' ';
	line = ' ';
	while (1)
	{
		ifs.get(ch);
		if (ch == 'v') break;
		getline(ifs, line);
	}
	int v = 0;
	ifs.get(ch);
	ifs >> v;
	Side_Num = v;

	//初始化地图
	double w;
	string s1, s2;
	for (int i = 0; i < Side_Num; ++i)
	{
		Node* x = new Node();
		Node* y = new Node();
		//边s1,s2对应(x -w- y)
		ifs >> s1 >> s2 >> w;
		//x为始点,y为末点, 头插法
		y->index = Find(Net, s2);
		y->weight = w;
		y->next = Net[Find(Net, s1)].First;
		Net[Find(Net, s1)].First = y;
		//y为始点,x为末点
		x->index = Find(Net, s1);
		x->weight = w;
		x->next = Net[Find(Net, s2)].First;
		Net[Find(Net, s2)].First = x;
	}
}

//主函数 
int main()
{
	ios::sync_with_stdio(0); cin.tie(0);
	static Park_guide test;
	int temp = -1;
	cout << "欢迎来到公园!!!!" << endl<<endl;
	while (1)
	{
		temp = -1;
		cout << "*****************************" << endl;
		cout << "* 请输入序号:              *" << endl;
		cout << "* 1:游览两个景点的最短路径 *" << endl;
		cout << "* 2:游览路线(入口到出口)   *" << endl;
		cout << "* 0:quit                   *" << endl;
		cout << "*****************************" << endl;
		scanf("%d", &temp); 
		if ( temp == 1 || temp == 2 || temp == 0)
			switch (temp)
			{
		
				case 1: 
				{
					cout << "请输入您要游览的两个景点(空格间隔):" << endl;
					string s1, s2;
					cin >> s1 >> s2;
					test.Two_Mi_line(s1, s2);
					break;
				}
				case 2:
				{
					cout << "为您输入最多"<<test.Re_Num()<<"条的路径" << endl;
					test.Inout_ways();
					break;
				}
				case  0: 
				{
					cout << "正常退出" << endl;
					return 0;
				}
			}
		else
		{
			cout << "非正常退出" << endl;
			return 0;
		}
	}
	
	return 0;
}

到这也就结束啦,这篇文章也是我第一次发表的,看起来有的多,有的乱,哪里有错误或不解的地方可以评论告诉我,我的确尽了很大努力了,就当作是锻炼自己。希望我自己的拙见能对大家有帮助吧,祝大家身体健康,快乐每一天! 

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值