abyssfall的博客_CSDN博客-计科学子的头发账本,代码存档领域博主
作者: 冯向阳时间限制: 1S章节: 课程设计
问题描述 :
在使用图的邻接矩阵ADT的基础上,设计能指定起始结点u的Prim算法,用以求无向网的最小生成树,并以文本形式输出生成树中各条边以及它们的权值。将此算法加入到邻接矩阵ADT中,在邻接矩阵ADT中提供一个公有的成员函数Prim(u, adjvex, lowcost)。
提示:
(1)Prim算法的基本思想是从结点的角度出发。初始时,生成树中一个结点也没有,然后将一个个结点加入生成树,直到所有的结点都被加入,生成树就形成了。具体来讲,Prim算法由以下几个步骤组成:
1)初始时,生成树的结点集U为空;
2)随意选择一个结点,加入结点集V,然后重复下列工作,直到U=V;
3)把(u,v)加入生成树的边集,v加入到U。
(2)在Prim算法的执行过程中,需要保存哪些结点在U中,哪些结点不在U中的信息。这可以用一个布尔型的一维数组flag来保存。如果结点i已在生成树中,则flag[i]的值为true,否则为false。
(3)在Prim算法的执行过程中,还需要保存U中的结点到V-U中结点的权值最小的边,这可以用2个一维数组lowcost和adjvex来记录。lowcost[i]表示U中的结点到结点i的所有边中的最小权值。adjvex[i]表示从U中的哪一个结点出发到结点i的权值是最小的。
参考函数原型:
template<class TypeOfVer, class TypeOfEdge>
bool adjmatrix_graph<TypeOfVer, TypeOfEdge>::Prim(int u, int adjvex[], TypeOfEdge lowcost[]);
输入说明 :
第一行:图的类型
第二行:结点数
第三行:结点集
第四行:无边标记
第五行:边数
第六行:边集
第七行:权集
第八行:起始结点序号u
输出说明 :
第一行:图的类型
第二行:顶点集
空行
第三行:邻接矩阵(列与列之间用格式控制符'\t'分隔)
第四行:各条边以及它们的权值,输出格式为:
(c,b,5),(d,c,3),(e,d,8),(a,e,14),(d,f,21),(e,g,16)
其中,每个括号内的信息为一条边,格式为(邻接点, 自己, 权值),边的输出顺序为按照(自己这个)点的编号顺序输出。“自己这个”不包括起始点,比如范例中输出的边不包括起始点a,也就是没有(*,a,*)。
UDN
7
a b c d e f g
9999
11
0 1
0 4
0 6
1 2
1 3
1 4
2 3
3 4
3 5
4 6
5 6
19 14 18 5 7 12 3 8 21 16 27
0
UDN
a b c d e f g
9999 19 9999 9999 14 9999 18
19 9999 5 7 12 9999 9999
9999 5 9999 3 9999 9999 9999
9999 7 3 9999 8 21 9999
14 12 9999 8 9999 9999 16
9999 9999 9999 21 9999 9999 27
18 9999 9999 9999 16 27 9999
(c,b,5),(d,c,3),(e,d,8),(a,e,14),(d,f,21),(e,g,16)
#include <bits/stdc++.h>
using namespace std;
const int MAX = 1000;
string kind;
int m = 0;
int arr[MAX][2] = {0}; //输入权集的对应横纵坐标
int none, u; //无边标记
int all[MAX][MAX] = {0};
bool tree_flag[MAX][MAX] = {false};
struct E //边
{
int num;
int weight;
E *next = NULL;
};
struct V //结点
{
string data;
E *next = NULL;
};
struct graph
{
V m[MAX];
int number_of_V;
int number_of_E;
};
void addweight_no_direction(graph &g);
void addweight(graph &g);
void creat_no_directioin(graph &g)
{
int v, f;
cin >> v;
g.number_of_V = v;
for (int i = 0; i < v; i++)
{
cin >> g.m[i].data;
}
//输入顶点字母
cin >> none; //输入无边标记
cin >> f;
g.number_of_E = f;
for (int i = 0; i < f; i++)
{
int v1, v2;
cin >> v1 >> v2;
arr[i][0] = v1;
arr[i][1] = v2;
E *p1 = new E;
p1->num = v2;
p1->next = g.m[v1].next;
g.m[v1].next = p1;
E *p2 = new E;
p2->num = v1;
p2->next = g.m[v2].next;
g.m[v2].next = p2;
}
addweight_no_direction(g);
}
void addweight_no_direction(graph &g)
{
for (int j = 0; j < g.number_of_E; j++)
{
int temp = 0;
cin >> temp;
int number1 = arr[j][0];
int number2 = arr[j][1];
all[number1][number2] = temp;
all[number2][number1] = temp;
E *p;
E *p1;
p = g.m[number1].next;
p1 = g.m[number2].next;
while (p)
{
if (p->num == number2)
{
p->weight = temp;
}
p = p->next;
}
while (p1)
{
if (p1->num == number1)
{
p1->weight = temp;
}
p1 = p1->next;
}
}
}
void creat_direction(graph &g)
{
int v;
int f;
cin >> v;
g.number_of_V = v;
for (int i = 0; i < v; i++)
{
cin >> g.m[i].data;
} //输入顶点字母
cin >> none; //输入无边标记
cin >> f;
g.number_of_E = f;
for (int i = 0; i < f; i++)
{
int v1, v2;
cin >> v1 >> v2;
arr[i][0] = v1;
arr[i][1] = v2;
E *p1 = new E;
p1->num = v2;
p1->next = g.m[v1].next;
g.m[v1].next = p1;
}
addweight(g);
}
void addweight(graph &g)
{
for (int j = 0; j < g.number_of_E; j++)
{
int temp = 0;
cin >> temp;
int number1 = arr[j][0];
int number2 = arr[j][1];
all[number1][number2] = temp;
E *p;
p = g.m[number1].next;
while (p)
{
if (p->num == number2)
{
p->weight = temp;
}
p = p->next;
}
}
}
void printV(graph g)
{
int m = 0;
for (int h = 0; h < g.number_of_V; h++)
{
if (m == 1)
{
cout << " ";
}
m = 1;
cout << g.m[h].data;
}
cout << endl
<< endl;
}
void printgraph(graph g)
{
for (int i = 0; i < g.number_of_V; i++)
{
for (int j = 0; j < g.number_of_V; j++)
{
if (all[i][j] == 0)
{
cout << none;
}
else
{
cout << all[i][j];
}
cout << '\t';
}
cout << endl;
}
}
void print_tree(graph g)
{
bool f = false;
for (int i = 0; i < g.number_of_V; i++)
{
if (i == u)
{
continue; //根节点不输出
}
for (int j = 0; j < g.number_of_V; j++)
{
if (tree_flag[i][j] == true)
{
if(f == true)
{
cout<<",";
}
else
{
f = true;
}
cout << "(" << g.m[j].data << "," << g.m[i].data << "," << all[i][j] << ")";
}
}
}
}
int find(graph g, string data)
{
for (int i = 0; i < g.number_of_V; i++)
{
if (g.m[i].data == data)
{
return i;
}
}
return -1;
}
void prim(graph g, int u)
//核心思路:划分已添加和未添加两部分,
{
bool flag_judge[g.number_of_V] = {false};
//标记已添加至二叉树的结点,每个点都要被添加且仅添加一次,以true的结点遍历寻找指向false结点的最小边
//int flag_num[g.number_of_V] = {0};
//标记已添加结点的已连接边,一旦到2,不再加边;结束后为0,视为叶子结点
int now_node = 1; //记录一下循环结束条件
vector<V> now_tree;
now_tree.push_back(g.m[u]);
flag_judge[u] = true; //从这里开始
while (now_node < g.number_of_V) //还有节点没有被加入二叉树
{
int start_node = -1, min_node = -1, min_value = none;
for (int i = 0; i < now_node; i++)
{
//注意一下这里 是不是把nowtree的编号和g的编号弄混了
//if (flag_num[find(g, now_tree[i].data)] == 2)
//{
// continue;
//} //这个结点已经到达构建二叉树的上限了
//最小生成树不用限死在二叉树啊!!!!!!!!!啊!!!!!!!
E *p = now_tree[i].next;
while (p != NULL)
{
if (p->weight < min_value && flag_judge[p->num] == false) //比现在的边小,且终点未被加入
{
min_value = p->weight;
min_node = p->num;
start_node = i;
}
p = p->next;
}
} //找出指向未加入结点的最小边
start_node = find(g, now_tree[start_node].data);
flag_judge[min_node] = true;
//flag_num[start_node]++;
tree_flag[min_node][start_node] = true;
//标记该条边和该点入选
now_tree.push_back(g.m[min_node]);
now_node++;
}
}
int main()
{
cin >> kind;
if (kind == "DN")
{
cout << "DN" << endl;
graph g;
creat_direction(g);
printV(g);
printgraph(g);
cin >> u;
prim(g, u);
print_tree(g);
}
else if (kind == "UDN")
{
cout << "UDN" << endl;
graph g;
creat_no_directioin(g);
printV(g);
printgraph(g);
cin >> u;
prim(g, u);
print_tree(g);
}
getchar();
getchar();
return 0;
}