关于图的几个概念定义:

        

关于图的几个概念定义:

  • 连通图:在无向图中,若任意两个顶点vi与vj都有路径相通,则称该无向图为连通图。
  • 强连通图:在有向图中,若任意两个顶点vi与vj都有路径相通,则称该有向图为强连通图。
  • 连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
  • 生成树一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
  • 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。

构造最小生成树的准则有3条:

(1)必须只使用该网络中的边来构造最小生成树。

(2)必须使用且仅使用n-1条边来连接网络中的n个顶点。

(3)不能使用产生回路的边。



下面介绍两种求最小生成树算法

1 Prim(普利姆算法)算法--加点法

此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。

Prim算法从任意一个顶点开始,每次选择一个与当前顶点集最近的一个顶点,并将两顶点之间的边加入到树中。Prim算法在找当前最近顶点时使用到了贪婪算法。

实现过程:

图例 说明 不可选 可选 已选(Vnew

此为原始的加权连通图。每条边一侧的数字代表其权值。 - - -

顶点D被任意选为起始点。顶点ABEF通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。 C, G A, B, E, F D

下一个顶点为距离DA最近的顶点。BD为9,距A为7,E为15,F为6。因此,FDA最近,因此将顶点F与相应边DF以高亮表示。 C, G B, E, F A, D
算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。 C B, E, G A, D, F

在当前情况下,可以在CEG间进行选择。CB为8,EB为7,GF为11。E最近,因此将顶点E与相应边BE高亮表示。 C, E, G A, D, F, B

这里,可供选择的顶点只有CGCE为5,GE为9,故选取C,并与边EC一同高亮表示。 C, G A, D, F, B, E

顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EG G A, D, F, B, E, C

现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。 A, D, F, B, E, C, G
 #include<stdio.h>
#include<string.h>
#define MAX 0x3f3f3f3f
using namespace std;
int logo[];///用0和1来表示是否被选择过
int map1[][];
int dis[];///记录任意一点到这一点的最近的距离
int n,m;
int prim()
{
int i,j,now;
int sum=;
for(i=;i<=n;i++)///初始化
{
dis[i]=MAX;
logo[i]=;
}
for(i=;i<=n;i++)
{
dis[i]=map1[][i];
}
dis[]=;
logo[]=;
for(i=;i<n;i++)///循环查找
{
now=MAX;
int min1=MAX;
for(j=;j<=n;j++)
{
if(logo[j]==&&dis[j]<min1)
{
now=j;
min1=dis[j];
}
}
if(now==MAX)///防止不成图
{
break;
}
logo[now]=;
sum=sum+min1;
for(j=;j<=n;j++)///填入新点后更新最小距离,到顶点集的距离
{
if(logo[j]==&&dis[j]>map1[now][j])
{
dis[j]=map1[now][j];
}
}
}
if(i<n)
{
printf("?\n");
}
else
{
printf("%d\n",sum);
}
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)///n是点数
{
if(m==)
{
break;
}
memset(map1,0x3f3f3f3f,sizeof(map1));///map是邻接矩阵储存图的信息
for(int i=;i<m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(c<map1[a][b])///防止出现重边
{
map1[a][b]=map1[b][a]=c;
}
}
prim();
}
return ;
}

邻接表实现:

 #include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
struct node
{
int end;///终点
int power;///权值
} t;
int n;///n为点数
vector<node>q[];///邻接表储存图的信息
int dis[];///距离
int vis[];///标记数组
void prime()
{
int i,len,j,pos,sum,start;
memset(vis,,sizeof(vis));
sum=;
start=;///任意取起点
for(i=; i<=n; i++)
{
dis[i]=INF;
}
len=q[start].size();
for(i=; i<len; i++)///从任意起点开始的dis数组更新
{
if(q[start][i].power<dis[q[start][i].end])
{
dis[q[start][i].end]=q[start][i].power;
}
}
vis[start]=;
for(j=; j<n-; j++)
{
int pos,min=INF;
for(i=; i<=n; i++)
{
if(vis[i]!=&&dis[i]<min)
{
min=dis[i];
pos=i;///找到未访问节点中权值最小的
}
}
if(pos==INF)///防止不成图
{
break;
}
vis[pos]=;
sum=sum+min;
len=q[pos].size();///再次更新dis数组
for(j=; j<len; j++)
{
if(vis[q[pos][j].end]==&&dis[q[pos][j].end]>q[pos][j].power)
{
dis[q[pos][j].end] = q[pos][j].power;
}
}
}
if(j<n)
{
printf("?\n");
}
else
{
printf("%d\n",sum);
}
}
int main()
{
int m,i;
int begin,end,power;
int a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=; i<=n; i++)
{
q[i].clear();///将victor数组清空
}
for(i=; i<m; i++)
{
scanf("%d%d%d",&begin,&end,&power);///输入
t.end=end;
t.power=power;
q[begin].push_back(t);
t.end=begin;///无向图
t.power=power;
q[end].push_back(t);
}
prime();
}
return ;
}

这里再给出一个没有使用标记数组的代码:

int prim(int s)
{
int i,j,sum=;
int now;
for(i=;i<=n;i++)
{
closest[i]=INT_MAX;
}
for(i=;i<=n;i++)
{
closest[i]=map[s][i];
}
closest[s]=;
for(i=;i<n;i++)//这里的i代表的是边数,有n个点就会有n-1条边
{
int min=INT_MAX;
for(j=;j<=n;j++)
{
if(closest[j]&&closest[j]<min)
{
min=closest[j];
now=j;//找到所需的最小边
}
}
sum+=min;
closest[now]=;//将找到的边加入到最小生成树之中
for(j=;j<=n;j++)//找到新的点加入已选点集合之后,更新该点到未选点集合的距离
{
if(map[now][j]&&map[now][j]<closest[j])
{
closest[j]=map[now][j];
}
}
}
return sum;
}

Kruskal(克鲁斯卡尔)算法--加边法

1.概览

  Kruskal算法是一种用来寻找最小生成树的算法,在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边。

2.实现过程

1).记Graph中有v个顶点,e个边

2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边

3).将原图Graph中所有e个边按权值从小到大排序

4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中  if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中   添加这条边到图Graphnew

  图例描述:

首先第一步,我们有一张图Graph,有若干点和边

将所有的边的长度排序,用排序的结果作为我们选择边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行选择,排序完成后,我们率先选择了边AD。这样我们的图就变成了下图

在剩下的变中寻找。我们找到了CE。这里边的权重也是5

依次类推我们找到了6,7,7,即DF,AB,BE。

下面继续选择, BC或者EF尽管现在长度为8的边是最小的未选择的边。但是现在他们已经连通了(对于BC可以通过CE,EB来连接,类似的EF可以通过EB,BA,AD,DF来接连)。所以不需要选择他们。类似的BD也已经连通了(这里上图的连通线用红色表示了)。最后就剩下EG和FG了。当然我们选择了EG。

代码:(利用并查集来实现)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,sum;
struct node
{
int start;///起点
int end;///终点
int power;///权值
}edge[];
int pre[];
int cmp(node a,node b)
{
return a.power<b.power;///按权值排序
}
int Find(int x)///并查集找祖先
{
if(x!=pre[x])///递归法
{
pre[x]=Find(pre[x]);
}
return pre[x]; /*int a;///循环法
a=x;
while(pre[a]!=a)
{
a=pre[a];
}
return a;*/
}
void Union(int x,int y,int n)
{
int fx =Find(x);
int fy =Find(y);
if(fx!=fy)
{
pre[fx]=fy;
sum+=edge[n].power;
}
}
int main()
{
int i;
while(scanf("%d",&n)!=EOF)
{
sum=;
m=n*(n-)/;
for(i=;i<=m;i++)
{
scanf("%d%d%d",&edge[i].start,&edge[i].end,&edge[i].power);
}
for(i=;i<=m;i++)///并查集的初始化
{
pre[i]=i;
}
sort(edge+,edge+m+,cmp);
for(i=;i<=m;i++)
{
Union(edge[i].start,edge[i].end,i);
}
printf("%d\n",sum);
}
return ;
}

最小生成树(Kruskal和Prim算法)的更多相关文章

  1. 最小生成树——Kruskal与Prim算法

    最小生成树——Kruskal与Prim算法 序: 首先: 啥是最小生成树??? 咳咳... 如图: 在一个有n个点的无向连通图中,选取n-1条边使得这个图变成一棵树.这就叫“生成树”.(如下图) 每个 ...

  2. 关于最小生成树 Kruskal 和 Prim 的简述(图论)

    模版题为[poj 1287]Networking. 题意我就不说了,我就想简单讲一下Kruskal和Prim算法.卡Kruskal的题似乎几乎为0.(●-`o´-)ノ 假设有一个N个点的连通图,有M条 ...

  3. Kruskal和Prim算法求最小生成树

    Kruskal算法求最小生成树 测试数据: 5 6 0 1 5 0 2 3 1 2 4 2 4 2 2 3 1 1 4 1 输出: 2 3 1 1 4 1 2 4 2 0 2 3 思路:在保证不产生回 ...

  4. 最小生成树的kruskal、prim算法

    kruskal算法和prim算法 都说 kruskal是加边法,prim是加点法 这篇解释也不错:这篇 1.kruskal算法 因为是加边法,所以这个方法比较合适稀疏图.要码这个需要先懂并查集.因为我 ...

  5. 1.1.2最小生成树(Kruskal和Prim算法)

    部分内容摘自 勿在浮沙筑高台 http://blog.csdn.net/luoshixian099/article/details/51908175 关于图的几个概念定义: 连通图:在无向图中,若任意 ...

  6. 算法导论--最小生成树(Kruskal和Prim算法)

    转载出处:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175 关于图的几个概念定义: 连通图:在无向图中,若任意两个顶 ...

  7. 数据结构与算法--最小生成树之Prim算法

    数据结构与算法--最小生成树之Prim算法 加权图是一种为每条边关联一个权值或称为成本的图模型.所谓生成树,是某图的一棵含有全部n个顶点的无环连通子图,它有n - 1条边.最小生成树(MST)是加权图 ...

  8. [讲解]prim算法<最小生成树>

    最小生成树的方法一般比较常用的就是kruskal和prim算法 一个是按边从小到大加,一个是按点从小到大加,两个方法都是比较常用的,都不是很难... kruskal算法在本文里我就不讲了,本文的重点是 ...

  9. hiho 1097 最小生成树一·Prim算法 (最小生成树)

    题目: 时间限制:10000ms 单点时限:1000ms 内存限制:256MB   描述 最近,小Hi很喜欢玩的一款游戏模拟城市开放出了新Mod,在这个Mod中,玩家可以拥有不止一个城市了! 但是,问 ...

随机推荐

  1. MySQL学习之用户管理

    用户权限管理 用户权限管理:在不同的项目中给不同的角色(开发者)不同的操作权限,为了保证数据库数据的安全. 简单点说:有的用户可以访问并修改这个数据,而有些用户只能去查看数据,而不能修改数据.就如同博 ...

  2. 误操作yum导致error: rpmdb解决方法

    错误:[root@test ~]# yum makecache error: rpmdb: BDB0113 Thread/process 18967/139716328294400 failed: B ...

  3. nuxt 优化项:禁用js的预加载

    这里有个nuxt和vue不同的地方,这个地方很有意思,官方的中文文档说得蜜汁自信 ------------------------------- In production, nuxt.js uses ...

  4. MFC非模态添加进程控件方法一(线程方法)

    由于非模态对话框的自己没有消息循环,创建后无法进行消息处理.需要和父窗口共用消息循环.如果单独在子窗口进行控件由于自己没有单独的消息循环,更新是无法进行的. 如果在父窗口更新控件会造成程序假死.如以下 ...

  5. STM32学习日志

    今天是开学第二周周末 写这篇博客纯属是为了记住一些学到的知识,大佬勿喷.. 首先学32要知道一些选型知识,32中常用的芯片类型(某宝常卖的开发板芯片大多是这几种):C8T6,RBT6,RCT6,VET ...

  6. 【8086汇编-Day7】关于多个段的程序的实验

    实验一 实验二 实验三 实验四 实验五 实验六 总结 在集成环境下,内存从0770段开始按照段的先后顺序和内容多少分配,并且分配的都是16的倍数 关于实际占用的空间公式的话其实极容易想到(假设有N个字 ...

  7. 【blockly教程】第四章 Blockly之选择结构

    今天,我们通过一个游戏来学习选择结构,游戏的地址如下:https://blockly-games.appspot.com/bird?lang=en本游戏分为10关:主要游戏规则如下:①主界面是游戏的运 ...

  8. 版本控制工具——Git的拓展使用

    一.使用Github 通过前面两节已经配置了SSH Key与Github上的相关设置,接下来介绍常用的使用 使用Fork克隆一份到本地仓库 之后可以在自己的仓库克隆一份到本地 git clone gi ...

  9. 离线安装Sharepoint工具

    1. 首先安装操作系统,Windows Server 2008 R2,可以是企业版,也可以是数据中心版.然后再安装上SP1. 2. 在"服务管理"里面,添加角色,安装IIS.    ...

  10. 「日常训练」Divisibility by Eight(Codeforces Round 306 Div.2 C)

    题意与分析 极简单的数论+思维题. 代码 #include <bits/stdc++.h> #define MP make_pair #define PB emplace_back #de ...