数据结构实验3(飞机最少环城次数问题)

本文介绍如何运用图算法中的Dijkstra算法来解决飞机旅行中寻找换乘次数最少的路线问题。通过建立有向图表示城市航线,并在MGraph类中实现Choose和Dijkstra函数,以找到最佳飞行方案。

使用图算法解决应用问题: 设有n个城市, 编号为0 ~ n - 1, m条航线的起点和终点由用户输入提供. 寻找一条换乘次数最少的线路方案.

使用有向图表示城市间的航线, 只要两城市之间有航班, 则图中这两点间存在一条权为1的边. 用Dijkstra算法实现求最少换乘次数.

在MGraph类中增加Choose函数以及Dijkstra函数即可.

实现代码:

#include "iostream"
#include "cstdio"
#include "cstring"
#include "algorithm"
#include "queue"
#include "stack"
#include "cmath"
#include "utility"
#include "map"
#include "set"
#include "vector"
#include "list"
#include "string"
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
enum ResultCode { Underflow, Duplicate, Failure, Success, NotPresent, OutOfBounds };
template <class T>
class Graph
{
public:
	virtual	~Graph() {};
	virtual ResultCode Insert(int u, int v, T w) = 0;
	virtual ResultCode Remove(int u, int v) = 0;
	virtual bool Exist(int u, int v) const = 0;
	/* data */
};
template <class T>
class MGraph: public Graph<T>
{
public:
	MGraph(int mSize, const T& noedg);
	~MGraph();
	ResultCode Insert(int u, int v, T w);
	ResultCode Remove(int u, int v);
	bool Exist(int u, int v) const;
	int Choose(int *d, bool *vis);
	void Dijkstra(int v, int *d, int *path);
	int Vertices() const { return n; }
	void Output();
protected:
	T **a;
	T noEdge;
	int n, e;
	/* data */
};
template <class T>
void MGraph<T>::Output()
{
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j)
			if(a[i][j] == noEdge) cout << "NE\t";
			else cout << a[i][j] << "\t";
		cout << endl;
	}
	cout << endl << endl << endl;
}
template <class T>
MGraph<T>::MGraph(int mSize, const T &noedg)
{
	n = mSize, e = 0, noEdge = noedg;
	a = new T *[n];
	for(int i = 0; i < n; ++i) {
		a[i] = new T[n];
		for(int j = 0; j < n; ++j)
			a[i][j] = noEdge;
		a[i][i] = 0;
	}
}
template <class T>
MGraph<T>::~MGraph()
{
	for(int i = 0; i < n; ++i)
		delete []a[i];
	delete []a;
}
template <class T>
bool MGraph<T>::Exist(int u, int v) const
{
	if(u < 0 || v < 0 || u > n - 1 || v > n - 1 || u == v || a[u][v] == noEdge) return false;
	return true;
}
template <class T>
ResultCode MGraph<T>::Insert(int u, int v, T w)
{
	if(u < 0 || v < 0 || u > n - 1 || v > n - 1 || u == v) return Failure;
	if(a[u][v] != noEdge) return Duplicate;
	a[u][v] = w;
	e++;
	return Success; 
}
template <class T>
ResultCode MGraph<T>::Remove(int u, int v)
{
	if(u < 0 || v < 0 || u > n - 1 || v > n - 1 || u == v) return Failure;
	if(a[u][v] == noEdge) return NotPresent;
	a[u][v] = noEdge;
	e--;
	return Success;
}
template <class T>
int MGraph<T>::Choose(int *d, bool *vis)
{
	int pos = -1;
	T m = INF;
	for(int i = 0; i < n; ++i)
		if(d[i] <= m && !vis[i]) {
			m = d[i];
			pos = i;
		}
	return pos;
}
template <class T>
void MGraph<T>::Dijkstra(int v, int *d, int *path)
{
	if(v < 0 || v > n - 1) throw OutOfBounds;
	bool *vis = new bool[n];
	for(int i = 0; i < n; ++i) {
		vis[i] = false;
		d[i] = a[v][i];
		if(i != v && d[i] < INF) path[i] = v;
		else path[i] = -1;
	}
	vis[v] = true;
	d[v] = 0;
	for(int i = 1; i < n; ++i) {
		int x = Choose(d, vis);
		vis[x] = true;
		for(int j = 0; j < n; ++j)
			if(!vis[j] && d[x] + a[x][j] < d[j]) {
				d[j] = d[x] + a[x][j];
				path[j] = x;
			}
	}
}
int main(int argc, char const *argv[])
{
	int n, m;
	cout << "请输入城市个数:";
	cin >> n;
	cout << "请输入航线条数:";
	cin >> m;
	MGraph<int> A(n, INF);
	int c, f;
	cout << "请输入每条航线的起点和终点: " << endl;
	for(int i = 0; i < m; ++i)
	{
		cout << "航线" << i + 1 <<": ";
		cin >> c >> f;
		A.Insert(c, f, 1);
	}
	char s;
	int i = 0, j = 0;
	do{
		int v, w;
		cout << "请输入你的起点和终点:";
		cin >> v >> w;
		while(v < 0 || w < 0 || w > n - 1 || v > n - 1)
		{
			cout << "输入错误!请重新输入:";
			cin >> v >> w;
		}
		int *b = new int[n];
		int *d = new int[n];
		int *path = new int[n];
		A.Dijkstra(v, d, path);
		int e = n - 1;
		for(int j = 0; j < n; ++j)
			b[j] = -2;
		if(w != v)
		{		
			j = w;
			while(path[j] != -1)         
			{
				b[e] = path[j];
				e--;
				j = path[j];
			}
			if(e == n - 1 || d[j] == INF) cout << "该路间无线路!" << endl;
			else {
				cout << "从" << v << "到" << w << "的换乘次数最小的线路方案为:";
			    for(int k = 0; k < n; ++k)
					if(b[k] != -2) cout << b[k] << ",";
			    cout << w << endl;
			}
		}
		if(w == v) cout << "从" << v << "到" << w << "该路间无需乘飞机!" << endl;
		delete []b;
		delete []d;
		delete []path;
		cout << "请问是否继续查询路线?请输入Y/N:";
		cin >> s;
		while(s != 'Y' && s != 'y' && s != 'n' && s != 'N') {
			cout << "输入错误!请重新输入:";
			cin >> s;
		}
	}while(s == 'Y' || s == 'y');

	return 0;
}


#define MAXVEX 30 #define MAXCOST 1000 void prim(int c[MAXVEX][MAXVEX],int n) /*己知图的顶点为{1,2,...,n},c[i][j]c[j][i]为边(i,j)的权,打印最小生成树 的每边*/ { int i,j,k,min,lowcost[MAXVEX],closest[MAXVEX];; for (i=2;i<=n;i++) /*从顶点1开始*/ { lowcost[i]=c[1][i]; closest[i]=1; } closest[1]=0; for (i=2;i<=n;i++) /*从U之外求离U中某一顶点最近的顶点*/ { min=MAXCOST; j=1;k=i; while (j<=n) { if (lowcost[j]<min && closest[j]!=0) { min=lowcost[j]; k=j; } j++; } printf("(%d,%d) ",closest[k],k); /*打印边*/ closest[k]=0; /*k加入到U中*/ for (j=2;j<=n;j++) if (closest[j]!=0 && c[k][j]<lowcost[j]) { lowcost[j]=c[k][j]; closest[j]=k; } } } main() { int n=7,i,j,mx[MAXVEX][MAXVEX]; for (i=0;i<=n;i++) for (j=0;j0) i=set[i]; return(i); } kruskal(edgeset ge,int n,int e) /*ge表示的图是按权值从小到大排列的*/ { int set[MAXEDGE],v1,v2,i,j; for (i=1;i<=n;i++) set[i]=0; /*给set中的每个元素赋初值*/ i=1; /*i表示待获取的生成树中的边数,初值为1*/ j=1; /*j表示ge中的下标,初值为1*/ while (j<n && i<=e) /*按边权递增顺序,逐边检查该边是否应加入到生成树中*/ { v1=seeks(set,ge[i].bv); /*确定顶点v所在的连通集*/ v2=seeks(set,ge[i].tv); if (v1!=v2) /*当v1,v2不在同一顶点集合,确定该边应当选入生成树*/ { printf("(%d,%d) ",ge[i].bv,ge[i].tv); set[v1]=v2; j++; } i++; } } main() { int n=7,e=10; edgeset mx; mx[1].bv=4;mx[1].tv=6;mx[1].w=30; mx[2].bv=2;mx[2].tv=5;mx[2].w=40; mx[3].bv=4;mx[3].tv=7;mx[3].w=42; mx[4].bv=3;mx[4].tv=7;mx[4].w=45; mx[5].bv=1;mx[5].tv=2;mx[5].w=50; mx[6].bv=4;mx[6].tv=5;mx[6].w=50; mx[7].bv=3;mx[7].tv=4;mx[7].w=52; mx[8].bv=1;mx[8].tv=3;mx[8].w=60; mx[9].bv=2;mx[9].tv=4;mx[9].w=65; mx[10].bv=5;mx[10].tv=6;mx[10].w=70; printf("最小生成树边集:\n "); kruskal(mx,n,e); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值