今天学的内容是图论更深层次的东西:最小生成树。
先总结下概念:
一个无向图中,用且只用(n-1)条边连接n个点得到的一条路径(一棵树),叫做该图的生成树。所以由此概念就可以想到一个图中可能会有多棵生成树。
而当生成树的所有权值和与该图中其他生成树权值和最小时,则该树即为图中最小生成树。(终于解释完了)
好吧粘下官方版:
生成树:一个|V|个点的图,取其中|V|-1条边,并连接所有的顶点,则组成原图的一个生成树。
属性:|v|-1条边、连通、无环。
最小生成树:加权图的最小生成树是一棵生成树,其所有边的权值之和不会大于其它任何生成树。
简单讲:找出连接所有点的最低成本路线(我去真简单)
下面是算法方面:
1)普瑞姆算法(MST_prim)
思路:其实和dijkstra算法感觉超像啊。一样分(枚举/未枚举)两个集合,一样是枚举每个点。
–
1).将1号节点置入集合S中。
2).找到所有连接S中的节点和非S中的节点的边中的权值最小的那一条,并标记这条边,同时将连接的非S中的节点加入S集合。
3).重复2步骤,直到所有节点都在S中。
1).将1号节点置入集合S中。
2).找到所有连接S中的节点和非S中的节点的边中的权值最小的那一条,并标记这条边,同时将连接的非S中的节点加入S集合。
3).重复2步骤,直到所有节点都在S中。
void MST_Prim(int s)
{
memset(dis,10,sizeof(dis));
for(int i=1;i<=n;i++)
dis[i]=a[s][i];
memset(vis,0,sizeof(vis));
vis[s]=1;sumn=0;
for(int i=2;i<=n;i++)
{
int minn=a[0][0],c=0;
for(int j=1;j<=n;j++)
if((!vis[j])&&(dis[j]<minn))
{
minn=dis[j];
c=j;
}
vis[c]=1;
sumn+=minn;
for(int j=1;j<=n;j++)
if((a[c][j]<dis[j])&&(!vis[j]))
dis[j]=a[c][j];
}
}
2)克鲁斯卡尔算法(MST_Kruskar)
思路,貌似与 bellman·ford算法相似 ,以边集数组的概念存储/枚举每条边
但是要注意:
关键:加边时不能构成回路:边的两个顶点是否已在树中。
void MST_Kruskal()
{
int c=0;
sort(a+1,a+m+1,mycmp);
for(int i=1;i<=m;i++)
if(!(judge(a[i].v,a[i].u)))
{
merge(a[i].v,a[i].u);
summ=a[i].z;
if(++c==n-1) break;//只有这边i在最小生成树里时才减
}
}