我们知道,无向图的最小生成树的求法有Krusal和prime算法,一个是归点一个是归边,在具体实现上Krusal可以用并查集实现,难度不大。
这里稍微区别一下最短路径和最小生成树(因为我又搞混了23333)
最小生成树能够保证首先是树(对于n个顶点的图只有n-1条边),其次保证任意两个顶点之间都可达,再次保证这棵树的边权值之和为最小,但不能保证任意两点之间是最短路径;
最短路径保证从源点S到目地点D的路径最小(有向图中不要求终点能到起点),不保证任意两个顶点都可达;
最小生成树是用最小代价遍历整个图中所有顶点,所有的权值和最小。而最短路径只是保证出发点到终点的路径和最小,不一定要经过所有顶点;
最小生成树是到一群点(所有点)的路径代价和最小,是一个n-1条边的树,最短路径是从一个点到另一个点的最短路径;
总之,最小生成树一定保证包含所有结点,而最短路径则不然。若题目要求必须每个点都必须经过,则是MST的问题;若只要求起点终点的最小消费,则是最短路径问题。
那么,对于有向图求最小生成树应该如何求呢?有向图就意味着可能有环,比如下面的图:
1、2结点形成一个环,应该删除哪一条边呢?如果从3出发,就会删掉2->1的边,如果从4出发,就会删掉1->2的边。那么如果把1、2合成一个点,所以3的权值更新为9-3=6,同理4的权值变为7-4 = 3。相当于变相删除不需要走的边。
有向图的最小生成树(最小树形图)求解步骤如下:
- 先求出最短弧集合E0;
对于节点1 = min{3,9},结点2 = min{4,7},结点4 = 1
- 如果E0不存在,则图的最小树形图也不存在;
- 如果E0存在且不具有环,则E0就是最小树形图;
- 如果E0存在但是存在有向环,则把这个环收缩成一个点u,形成新的图G1,然后对G1继续求其的最小树形图,直到求到图Gi,如果Gi不具有最小树形图,那么此图不存在最小树形图,如果Gi存在最小树形图,那么逐层展开,就得到了原图的最小树形图。
对于上面那张图,整个过程直观地看就是这样:
下面是一个更科学的流程图:
附上代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
#define INF 0x7f7f7f7f
const int maxn = 105;
const int maxm = 100050;
int n, m;
int in[maxn]; //保存每个点的最小入权值
int pre[maxn]; //保存最小权值的父节点
int vis[maxn], id[maxn]; //vis是访问标识,id是重新分配的节点号
struct E{
int from,to,dis;
}edge[maxm];
int dist_mst(int n,int m,int root){
//节点数、边数、根节点
int ans = 0;
while(1){
for(int i = 0; i < n;++i)in[i] = INF;
for(int i = 0; i < m;++i){
int u = edge[i].from;
int v = edge[i].to;
//非根节点选出最小边,并记录父节点
if(in[v] > edge[i].dis && u != v){
in[v] = edge[i].dis;
pre[v] = u;
}
}
//若除了根节点外还有入度为0的结点,即孤立点,则没有最小生成树
for(int i = 0; i < n;++i){
if(i == root)continue;
if(in[i] == INF)return -1;