开学季Blog——记录我的第一次线上课设

写在前面

因为疫情的原因,去年还没到期末就提前放暑假了,期末的算法课设也被耽搁了,而且今年新学期,还是无法返校,所以学校最近在进行线上课设!

我的题目

我被分配到的题目是构造可以使N个城市连接的最小生成树问题

问题描述:给定一个地区的n个城市间的距离网,用Prim算法或Kruskal算法建立最小生成树,并计算得到的最小生成树的代价。(4)
要求:
1)城市间的距离网采用邻接矩阵表示,邻接矩阵的存储结构定义采用课本中给出的定义,若两个城市之间不存在道路,则将相应边的权值设为自己定义的无穷大值.要求在屏幕上显示得到的最小生成树中包括了哪些城市间的道路,并显示得到的最小生成树的代价。
2)表示城市间距离网的邻接矩阵(要求至少6个城市,10条边);
3)最小生成树中包括的边及其权值,并显示得到的最小生成树的代价。

过程

对于我这种菜鸟来说,这样的题目让我独立完成简直是太难了,我只好一边求助同学,一边在网上搜索,跟着一个b站的讲解视频学习了整整一个下午,才把这东西搞明白,一边思考,一边跟着敲,可算是在最后关头把这个课设完成了…

整体的设计思路就是从图中的一个起点a开始,把a加入U集合,然后,寻找从与a有关联的边中,权重最小的那条边并且该边的终点b在顶点集合:(V-U)中,我们也把b加入到集合U中,并且输出边(a,b)的信息,这样我们的集合U就有:{a,b},然后,我们寻找与a关联和b关联的边中,权重最小的那条边并且该边的终点在集合:(V-U)中,我们把c加入到集合U中,并且输出对应的那条边的信息,这样我们的集合U就有:{a,b,c}这三个元素了,一次类推,直到所有顶点都加入到了集合U。

下面是我的代码,跟大家分享一下:

#include
#include
#include
#include
using namespace std;
const int MAXV = 30;//最多顶点个数
const int INF = 32768;//表示极大值,即∞
struct Node
{
	int weight;
	int vex;//存放顶点编号
	int lowcost;//存放顶点权值
};
struct MGraph
{
	int vexs[MAXV], n, e; // exs表示顶点向量; n, e分别表示图的顶点数和边数
	Node city[MAXV][MAXV];//邻接矩阵
};
	Node closest[MAXV];//求最小生成树时的辅助数组
	int Locate(MGraph &g, int V) //求顶点位置函数
	{
		int j = -1, k;
		for (k = 0; k < g.n; k++)
			if (g.vexs[k] == V)
			{
				j = k;
				break;
			}
		return j;
	}

	MGraph CreateMap(MGraph &g)//创建图
	{
		int i, j, k, v1, v2, vex, m = 1;
		cout << "请输入城市的个数:";
		cin >> g.n;
		if (g.n <= 1)
		{
			cout << "最小生成树不存在!" << endl;
			return g;
		}
		else
		{
			cout << "请输入城市的边数:";
			cin >> g.e;
			for (i = 0; i < g.n; i++)//初始化邻接矩阵
				for (j = 0; j < g.n; j++)
					if (i == j)
						g.city[i][j].weight = 0;
					else
						g.city[i][j].weight = INF;
			cout << "请输入城市的顶点编号:";//输入图中的顶点编号
			for (i = 0; i < g.n; i++)
				cin >> g.vexs[i];
			cout << "输入每条边所对应的两个顶点及权值<格式:起点城市终点城市权值>!" << endl;
			for (k = 0; k < g.e; k++)
			{
				cout << "请输入第" << m++ << "条边:";
				cin >> v1 >> v2 >> vex;//输入一条边的两个顶点及权值
				i = Locate(g, v1);
				j = Locate(g, v2);
				g.city[i][j].weight = vex;
				g.city[j][i].weight = vex;
			}
			return (g);
		}
	}
	int Min(MGraph &g, Node closest[])//closest[]中权值最小的边
	{
		int i, min, j;
		min = INF;
		for (i = 0; i < g.n; i++)
			if (closest[i].lowcost < min && closest[i].lowcost != 0)
			{
				min = closest[i].lowcost;
				j = i;
			}
		return j;//返回权值最小的边在辅助数组中的位置
	}

	void prim(MGraph &g, int u)//从顶点u出发,按普里姆算法构造连通图g的最小生成树,并输出生成树的每条边
	{
		MGraph p;
		int i, j, k, k0, u0, v0, s = 0, n = 0;
		k = Locate(g, u);//k为顶点u的位置
		p.vexs[n++] = u;
		closest[k].lowcost = 0;//初始化U={u}
		for (i = 0; i < g.n; i++)
			if (i != k)//初始化closest[i]
			{
				closest[i].vex = u;
				closest[i].lowcost = g.city[k][i].weight;
			}
		for (j = 1; j <= g.n - 1; j++)//选择其余的n-1条边(n=g.n)
		{
			k0 = Min(g, closest);//closest[k0]中存有当前最小边(u0,v0)的信息
			u0 = closest[k0].vex;//u0∈U
			v0 = g.vexs[k0]; // 0∈V - U
			p.vexs[n++] = v0;
			s += closest[k0].lowcost;//求最小生成树的权值之和
			cout << "从编号为" << u0 << "的城市到编号为" << v0 << "的城市" << "的权值为" << closest[k0].lowcost << endl;//输出最小生成树的路径
			closest[k0].lowcost = 0;//将顶点v0纳入U集合
			for (i = 0; i < g.n; i++)//在顶点v0并入U之后,重新选择最小边
				if (g.city[k0][i].weight < closest[i].lowcost)
				{
					closest[i].lowcost = g.city[k0][i].weight;
					closest[i].vex = v0;
				}
		}
		cout << "\n最小生成树的权值之和为:" << s << endl;
	}
	void display(MGraph &g)//输出邻接矩阵算法
	{
		int i, j;
		for (i = 0; i < g.n; i++)
		{
			for (j = 0; j < g.n; j++)
				if (g.city[i][j].weight == INF)
					cout << "\t" << "∞";
				else
					cout << "\t" << g.city[i][j].weight;
			cout << endl;
		}
	}
	int main()//主函数
	{
		int st;
		MGraph g;
		cout << "\n*********************n个城市的最小生成树*****************" << endl;
		CreateMap(g);
		{
			cout << "\n该图所对应的邻接矩阵如下:" << endl;
			display(g);
			cout << endl << "请输入起点城市编号:";
			cin >> st;
			while (1) {
				if (st == 0)break;
				else if (st > g.n)
					cout << "输入起点城市编号有错,请重新输入!" << endl;
				else
				{
					prim(g, st);
					cout << "\n*************************************************" << endl;
				}
				cout << endl << "请继续输入起点城市,否则输入0退出!" << endl;
				cin >> st;
			}
		}
	}	

总结

这是一次不一样的课设,也是难得的一次体验,更让我了解到自己的不足,我以后一定要好好把自己的基础打好。

你可能感兴趣的:(算法,图论,c++)