直接上报告和代码~~~~
【问题描述】
设某城市有n个居民区,连接两居民区的道路有m条,长度不一。现需沿道路铺设煤气管道,使得全部居民区连通,且铺设所需管道总长最小。
【基本要求】
采用耗费邻接矩阵为储存结构表达居民点及道路构成的网,求解最小生成树
【设计思路】
我使用了Dijkstra算法。
我的设计思路大致分三步:
- 第一步生成邻接矩阵:
- 首先根据点数point创建一个point+1Xpoint+1大小的int类型的矩阵DisMat,比如DisMat[i][j]和DisMat[j][i]就代表点i和j之间的距离,前提是数组中这两个值不为0,如果为0就代表两个点i和j之间没有通路。
- 第二步生成路径表:
- 这个路径表Array中的每个结点仍为Node*类型的,比如定义一个Node*类型的指针p,p->data为int类型,代表它是哪个点(其实这个路径表是顺序存储的,Array[i]就代表第i个点,加上一个data是方便回溯时用)那么p->distance代表当前状态下这个点到点1的距离,p->check代表它是否被遍历过,为bool类型的,p->ahead代表它之前的结点。
- 第三步进行遍历:
- 首先在路径表Array中找distance最小的结点,当然是点1本身了(默认点1为源点,其它点的距离都是9999),然后在DisMat中找和1相邻的点,并存到路径表中,它们的ahead都是结点1,然后再找distance最小的结点,比如点3,这个点是和1最近的点,把它的check改为false,意思就是点1到点3的最短路径找到了,然后在DisMat中找和点3相邻的点,如果某个点4也和1相邻,但是经过点3到点4的距离比从点1直接到点4的距离近,那么就更新结点4的distance和ahead,然后重复在路径表中找distance最小的过程,直到路径表中的每个结点的check都为false。
- 关于输出的过程,因为每个结点都记录了它上一个结点的地址,那么回溯输出就行。加一个小小的数组把输出方式反向比如3←4←1变成1→4→3就行了。
【测试数据】
【输出结果】
【总结体会】
其实这个程序和必做题一样有很多问题,比如有多个全局变量,限制了函数的局限性。其次就是点的输入有很多要求,必须从1开始依次输入,比如1,2,3,4,…不能隔点输入,比如1,3,7,…还有的问题就是不能判断是否是两片或者多片区域,比如1,2,3,4之间有通路,5,6,7,8之间有通路,但是两片区域之间没有通路等等,总之需要很多优化,或者还有很多地方没有考虑到~~~~比如可以将结点的data变成char类型,记录点的名称,比如北京西安南京~~~~
源代码如下:
#include<iostream>
using namespace std;
//邻接矩阵定义
int** DisMat;
//结点定义
struct Node {
int data;
int distance;
bool check;
Node* ahead;
};
//路径表定义
Node** Array;
//邻接矩阵的创建
bool Creat(int point, int line) {
if (point == 0 && line == 0) { cout << "该图为空图。" << endl; return false; }
if (point == 0 && line != 0 || point != 0 && line == 0) { cout << "该图是错的。" << endl; return false; }
DisMat = new int* [point+1];
for (int i = 0; i <= point; i++){
DisMat[i] = new int[point+1];
}
for (int i = 0; i <= point; i++) {
for (int j = 0; j <= point; j++) {
DisMat[i][j] = 0;
}
}
int head; int tail; int distance; int i = 1;
cout << "请从点1开始输入。" << endl;
while (line != 0) {
cout << "请输入第" << i << "条边的头、尾以及长度" << endl;
cin >> head >> tail >> distance;
DisMat[head][tail] = distance;
DisMat[tail][head] = distance;
i++;
line--;
}
return true;
}
//遍历更新
void Traverse(int point, int line) {
int min = 999;
Node* best = new Node;
for (int i = 1; i <= point; i++) {
if (Array[i]->check == true && (Array[i]->distance) < min) {
min = Array[i]->distance;
best = Array[i];
}
}
int bests = best->data;
Array[bests]->check = false;
for (int i = 1; i <= point; i++) {
if (DisMat[bests][i] != 0 && Array[i]->check == true && ((Array[i]->distance) > (DisMat[bests][i] + Array[bests]->distance))) {
Array[i]->distance = DisMat[bests][i] + Array[bests]->distance;
Array[i]->ahead = Array[bests];
}
}
for (int i = 1; i <= point; i++) {
if (Array[i]->check == true)Traverse(point, line);
}
}
//遍历准备
void bTraverse(int point, int line) {
Array = new Node * [point + 1];
for (int i = 1; i <= point; i++) {
Node* example = new Node;
example->distance = 9999;
example->ahead = NULL;
example->data = i;
example->check = true;
Array[i] = example;
}
Array[1]->distance = 0;
Traverse(point, line);
}
//程序准备及运行
int main() {
cout << "请输入点数和边数" << endl;
int point, line;
cin >> point >> line;
Creat(point, line);
bTraverse(point, line);
for (int i = 2; i <= point; i++) {
if (Array[i]->distance!=9999) {
int end[100] = { 0 }; int tran = 99;
Node* x = Array[i];
cout << "点1到点" << i << "的最短路径为:";
while (x->ahead != NULL) {
end[tran] = x->data;
tran--;
x = x->ahead;
}
end[tran] = x->data;
for (int i = 0; i <= 98; i++) {
if (end[i] != 0)cout << end[i] << "→";
}
cout << end[99] << endl;
cout << "点1到点" << i << "的最短距离为:";
cout << Array[i]->distance << endl;
}
else {
cout << "点1到点" << i << "没有通路。" << endl;
}
}
}