最小生成树:在一个给定的无向图G(V,E)中求一颗树T,使这棵树拥有图G中的所有顶点,且所有边都来自于图G中的边,并且满足整颗树的边权之和最小。
//图G一般设置为全局变量;数组d为顶点与集合S的最短距离
Prim(G, d[]){
初始化;
for (循环n次)
{
u = 使d[u]最小的海未被访问的顶点标号;
记u已被访问;
for (从u出发能到达的所有顶点v)
{
if(v未被访问&&以u为中介点使得v与集合S的最短距离d[v]更优){
将G[u][v]赋值给v与集合S的最短距离d[v];
}
}
}
}
const int MAXV = 1000;
const int INF = 0x3fffffff;
int G[MAXV][MAXV];//图
int n;//顶点个数
bool vis[MAXV] = {false};//记录顶点是否被访问
int d[MAXV];//顶点与集合S的最短距离
int prim(){//默认0号为初始点,函数返回最小生成树的边权之和
fill(d, d + MAXV, INF);
d[0] = 0;//只有0号顶点到集合S的距离为0,其余为INF
int ans = 0;//存放最小生成树的边权之和
for (int i = 0; i < n; ++i)
{
int u = -1, min = INF;
for (int j = 0; j < n; ++j)
{
if(vis[j] == false && d[j] < min){
u = j;
min = d[j];
}
}
//找不到小于INF的d[u],则剩下的顶点和集合S不连通
if(u == -1) return -1;
vis[u] = true;
ans += d[u];//将与集合S距离最小的边加入最小生成树
for (int v = 0; v < n; ++v)
{
//v未访问 && u能到达v && 以u为中介点可以是v离集合S更近
if(vis[v] == false && G[u][v] != INF && G[u][v] < d[v]){
d[v] = G[u][v];
}
}
}
return ans;
}
struct node
{
int v;
int dis;
};
const int MAXV = 1000;
const int INF = 0x3fffffff;
vector<node> G[MAXV];//图
int n;//顶点个数
bool vis[MAXV] = {false};//记录顶点是否被访问
int d[MAXV];//顶点与集合S的最短距离
int prim(){//默认0号为初始点,函数返回最小生成树的边权之和
fill(d, d + MAXV, INF);
d[0] = 0;//只有0号顶点到集合S的距离为0,其余为INF
int ans = 0;//存放最小生成树的边权之和
for (int i = 0; i < n; ++i)
{
int u = -1, min = INF;
for (int j = 0; j < n; ++j)
{
if(vis[j] == false && d[j] < min){
u = j;
min = d[j];
}
}
//找不到小于INF的d[u],则剩下的顶点和集合S不连通
if(u == -1) return -1;
vis[u] = true;
ans += d[u];//将与集合S距离最小的边加入最小生成树
for (int j = 0; j < G[u].size(); ++j)
{
int dis = G[u][j].dis;
int v = G[u][j].v;
//v未访问 && 以u为中介点可以是v离集合S更近
if(vis[v] == false && dis < d[v]){
d[v] = dis;
}
}
}
return ans;
}
输入:
6 10 //6个顶点,10条边。以下10行为10条边
0 1 4 //边0->1和1->0的边权为4,下同
0 4 1
0 5 2
1 2 6
1 5 3
2 3 6
2 5 5
3 4 4
3 5 5
4 5 3
输出:
15 //最小生成树边权
#include
#include
#include
using std::vector;
using std::fill;
struct node
{
int v;
int dis;
node(int _v, int _dis): v(_v), dis(_dis){}
};
const int MAXV = 1000;
const int INF = 0x3fffffff;
vector<node> G[MAXV];//图
int n;//顶点个数
bool vis[MAXV] = {false};//记录顶点是否被访问
int d[MAXV];//顶点与集合S的最短距离
int prim(){//默认0号为初始点,函数返回最小生成树的边权之和
fill(d, d + MAXV, INF);
d[0] = 0;//只有0号顶点到集合S的距离为0,其余为INF
int ans = 0;//存放最小生成树的边权之和
for (int i = 0; i < n; ++i)
{
int u = -1, min = INF;
for (int j = 0; j < n; ++j)
{
if(vis[j] == false && d[j] < min){
u = j;
min = d[j];
}
}
//找不到小于INF的d[u],则剩下的顶点和集合S不连通
if(u == -1) return -1;
vis[u] = true;
ans += d[u];//将与集合S距离最小的边加入最小生成树
for (int j = 0; j < G[u].size(); ++j)
{
int dis = G[u][j].dis;
int v = G[u][j].v;
//v未访问 && 以u为中介点可以是v离集合S更近
if(vis[v] == false && dis < d[v]){
d[v] = dis;
}
}
}
return ans;
}
int main(int argc, char const *argv[])
{
int m, u, v, w;
scanf("%d%d", &n, &m);
for (int i = 0; i < m; ++i)
{
scanf("%d%d%d", &u, &v, &w);
G[u].push_back(node(v,w));
G[v].push_back(node(u,w));
}
int ans = prim();
printf("%d\n", ans);
return 0;
}
/*
6 10
0 1 4
0 4 1
0 5 2
1 2 6
1 5 3
2 3 6
2 5 5
3 4 4
3 5 5
4 5 3
*/
struct edge
{
int u, v;//边的两个端点编号
int cost;//边权
};
bool cmp(edge a, edge b){
return a.cost < b.cost;
}
int krukal(){
令最小生成树的边权之和为ans、最小生成树的当前边数为Num_Edge;
将所有边按边权大小排序;
for(从小到大枚举所有的边){
if(当测试的两个端点在不同连通块中){
将测试边加入最小生成树中;
ans += 测试边的边权;
最小生成树的当前边权Num_Edge加1;
当边数Num_Edge等于顶点数减1时结束循环;
}
}
return ans;
}
const int MAXV = 120;//最多顶点数
const int MAXE = 1000;//最多边数
int father[MAXV];//并查集数组
struct edge
{
int u, v;//边的两个端点编号
int cost;//边权
}E[MAXE];
int findFather(int x){
int a = x;
while(x != father[x]){
x = father[x];
}
while(a != father[a]){
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
bool cmp(edge a, edge b){
return a.cost < b.cost;
}
int krukal(int n, int m){
int ans = 0, Num_Edge = 0;//ans:所求边权之和;Num_Edge:当前生成树的边数
for (int i = 1; i <= n; ++i)//假设顶点范围为1~n
{
father[i] = i;//并查集初始化
}
sort(E, E + m, cmp);//所有边权从小到大排序
for (int i = 0; i < m; ++i)//枚举所有边
{
int faU = findFather(E[i].u); //查询测试边的两个端点所在集合的根结点
int faV = findFather(E[i].v);
if(faU != faV){//如果不在一个集合中
father[faU] = faV;//合并集合(把测试边加入最小生成树)
ans += E[i].cost;//边权之和加入测试边的边权
Num_Edge++;//当前生成树边数加1
if(Num_Edge == n - 1){//边数等于顶点树-1时,结束算法
break;
}
}
}
if(Num_Edge != n - 1){//无法连接时,返回-1
return -1;
}else{//返回最小生成树的边权之和
return ans;
}
}
*时间复杂度主要在于对边进行排序,时间复杂度为O(ElogE)
输入:
6 10 //6个顶点、10条边,下面跟着10条无向边
0 1 4 //0号顶点与1号顶点的无向边的边权为4
0 4 1
0 5 2
1 2 1
1 5 3
2 3 6
2 5 5
3 4 5
3 5 4
4 5 3
输出:
11 //最小生成树的边权之和
#include
#include
using std::sort;
const int MAXV = 110;
const int MAXE = 10000;
int father[MAXV];
struct edge
{
int u, v;
int cost;
}E[MAXE];
bool cmp(edge a, edge b){
return a.cost < b.cost;
}
int findFather(int x){
int a = x;
while(x != father[x]){
x = father[x];
}
while(a != father[a]){
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
int kruskal(int n, int m){
int ans = 0, Num_Edge = 0;
for (int i = 0; i < n; ++i)
{
father[i] = i;
}
sort(E, E + m, cmp);
for (int i = 0; i < m; ++i)
{
int faU = findFather(E[i].u);
int faV = findFather(E[i].v);
if(faU != faV){
father[faU] = faV;
ans += E[i].cost;
Num_Edge++;
if(Num_Edge == n - 1){
break;
}
}
}
if(Num_Edge != n - 1){
return -1;
}else {
return ans;
}
}
int main(int argc, char const *argv[])
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < m; ++i)
{
scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].cost);
}
int ans = kruskal(n, m);
printf("%d\n", ans);
return 0;
}
/*
6 10
0 1 4
0 4 1
0 5 2
1 2 1
1 5 3
2 3 6
2 5 5
3 4 5
3 5 4
4 5 3
*/