Description
根据输入创建无向网。分别用Prim算法和Kruskal算法构建最小生成树。(假设:输入数据的最小生成树唯一。)
Input
顶点数n
n个顶点
边数m
m条边信息,格式为:顶点1 顶点2 权值
Prim算法的起点v
Output
输出最小生成树的权值之和
对两种算法,按树的生长顺序,输出边信息(Kruskal中边顶点按数组序号升序输出)
Input
Copy
6 v1 v2 v3 v4 v5 v6 10 v1 v2 6 v1 v3 1 v1 v4 5 v2 v3 5 v2 v5 3 v3 v4 5 v3 v5 6 v3 v6 4 v4 v6 2 v5 v6 6 v1
Output
Copy
15 prim: v1 v3 1 v3 v6 4 v6 v4 2 v3 v2 5 v2 v5 3 kruskal: v1 v3 1 v4 v6 2 v2 v5 3 v3 v6 4 v2 v3 5
Prim算法:
。
图片来源于知乎@夏虫,图片与样例无关
我个人感觉有点类似广度搜索
我们建立一个动态数组ss,第一个节点是0,放进去
ss:0
假设我们从0节点出发,以0为节点广度搜索与2的边是最短的,所以第一条边为0到2长度为1
把2放进ss里
ss:0 2
然后我们把0,2分别为中心都扫一遍没有进入树里面的节点,找到距离最短的边,就是0,2之外的节点,因为如果不除去0,2这个已经在树里的节点,那0为中心的时候扫到2就是最短的了。
然后我们找到2到5距离为1的边。将5,放入ss里
ss:0 2 5
然后重复上述步骤
0 2 1
2 5 4
5 3 2
2 1 5
1 4 3
这几条边生成最小生成树
kruskal算法:
这个算法很好想。首先你的会并查集,并查集是用来判断两条边是否有联系的,
假设1 2有关系,你可以把1的父节点当作2,然后2跟3有关系,那2的父节点是3。
1,2,3是不是都有关系,然后说难听点按上面的父亲关系来看,是不是1是3的孙子,2是3的儿子。
可以通过压缩路径的算法直接让1跟3有联系,这样判断1,2是否有关系的时候判断1,2的父节点是否相等就可以了。
实际算法实现。首先我们要按照边的长度去排序,拍好序之后是不是都是短的边在前面,这样方便我们选择。
然后判断这条边的两个节点是否已经有联系了,如果有就代表已经连接了,那就不能选,选了就重复了,成环了,如果没有就可以选,而且你从左往右数组遍历都是小的边在前。
如;
首先这个是无向图
可以得出6条边
1 2 1 a边
2 1 1 b边
2 3 2 c边
3 2 2 d边
1 3 3 e边
3 1 3 f边
我这已经将他排好序了,按照距离短的在前,但是距离相等的我按照字典序排了
我们选第一条a边
1 2 1
然后第二条边看b,但是1 2已经有边了,再选不就成环了吗,所以b不选
然后看c
2 3 2
2跟3没有联系,所以可以选,而且是能选中的最短了的是不是,
然后d边一样,都是构成环了,不选
后的1 3跟3 1的边都不选,因为1通过a c两条边已经让1 3建立联系了。
已经生成最小生成树了,n个节点用了n-1条边
是不是期间你不用考虑边长因为已经排好序了,只需要考虑两个点之间是否已经存在连接关系了。
代码如下:
希望如果要理解能粘贴在ide内,然后先不看函数体,看最开始的创建的变量,基本都带有解析,然后看main函数,然后遇到相关函数的时候往回找到函数,这样子方便理解。
#include <iostream>
#include <queue>
#include <vector>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 1e3 + 10;
int n, m;
int sum;///用于计算最小权值和
vector<string>ss;
string start;从哪个点开始
struct edge
{
int l, r;
int dis;
bool operator<(const edge& p)
{
if (dis < p.dis)
return true;
else if (dis > p.dis)
return false;
else
{
if (l < p.l)
return true;
else if (l > p.l)
return false;
else
{
if (r <= p.r)
return true;
else
return false;
}
}
}
};
edge shuzu[maxn];///kruskal算法的数组
int primtu[maxn][maxn];///prim算法的邻接矩阵
int visit[maxn];///prim算法判断节点的是否进树
int fa[maxn];///kruskal要用到并查集要用到的父节点
///prim图的初始化,还有k算法的父节点fa[]数组初始化
void irit()
{
for (int i = 0; i < n; i++)
{
visit[i] = 0;///prim算法的判断·这个点是否进树
fa[i] = i;//父节点都初始化为自己
for (int j = 0; j < n; j++)
{
primtu[i][j] = 0x3f3f3f;
}
}
}
//找对应下标对应的字符串
int findstr(string ch)
{
int len = ss.size();
for (int i = 0; i < len; i++)
{
if (ch == ss[i])
return i;
}
}
//prim算法实现
vector<edge>out;//因为要先计算权值和在输出,所以把输出都先存储
void prim()
{
int sindex=findstr(start);///startindex,开始的下标
vector<int>intree;///有点类似层次遍历
intree.push_back(sindex);///开始的节点已经进树了
visit[sindex] = 1;
while (out.size() < n-1)
{
int minm = 0x3f3f3f;
int len = intree.size();
int a=0, b=0;
for (int i = 0; i < len; i++)///找到相邻权值最小的那个节点
{
//行是intree[i],列是0到n
int hang = intree[i];
for (int j = 0; j < n; j++)
{
if (minm > primtu[hang][j]&&visit[j]==0)//j这个节点也要没进树里
{
a = hang;
b = j;
minm = primtu[hang][j];
}
}
}
//最后得出的就是相邻最小的节点
visit[b] = 1;
sum += minm;
edge nod;
nod.l = a, nod.r = b, nod.dis = minm;
out.push_back(nod);
intree.push_back(b);
}
}
/// <krusual>
int find_(int x)//并查集的查找
{
if (x == fa[x])
return x;
return fa[x] = find_(fa[x]);//压缩路径
}
void emerge_(int x, int y)///并查集的并,节点连接
{
int fx = find_(x), fy = find_(y);
if (fx != fy)
fa[fx] = fy;
}
void kruskal()
{
sort(shuzu, shuzu + 2*m);///按边长从小到大排序
int cnt = 0;
int i = 0;
while (cnt < n - 1)///n个点需要n-1条边
{
int a = shuzu[i].l, b = shuzu[i].r;
if (find_(a) != find_(b))//两个节点没有连接
{
emerge_(a, b);///那把两个节点连接
cout << ss[a] << " " << ss[b] << " " << shuzu[i].dis << endl;
cnt++;
}
i++;//i用来遍历数组的
}
}
/// </summary>
int main()
{
cin >> n;
irit();
for (int i = 0; i < n; i++)
{
string ch;
cin >> ch;
ss.push_back(ch);///这里是将一个下标对应一个字符串,这样可以在构建邻接矩阵的时候可以方便一些
}
cin >> m;
int k = 0;
for (int i = 0; i < m; i++)
{
string a, b;
int dis;
cin >> a >> b >> dis;
int indexa = findstr(a);//找出字符串对应下标
int indexb = findstr(b);
shuzu[k].l = indexa;//k算法要用的数组,l是左节点,r是右节点。dis是距离
shuzu[k].r = indexb;
shuzu[k].dis = dis;
k++;
shuzu[k].l = indexb;///这里因为是无向图,所以都创建一遍
shuzu[k].r = indexa;
shuzu[k].dis = dis;
k++;
primtu[indexa][indexb] = dis;///prim算法的无向图处理
primtu[indexb][indexa] = dis;无向图处理
}
cin >> start;
prim();///prim算法的实现
cout << sum << endl;//最小权值和
cout << "prim:" << endl;
int len = out.size();
for (int i = 0; i < len; i++)///因为是先输出权值和再输出节点,所以要用一个数组存着
{
cout << ss[out[i].l] << " " << ss[out[i].r] << " " << out[i].dis << endl;
}
cout << "kruskal:" << endl;
kruskal();///krusukal算法的实现
return 0;
}