关于这个东西,有的童鞋又要开始蒙了,最小生成树是个什么鬼?!
前面我们已经说过树是什么东西了,所谓最小生成树嘛,最小嘛,那就是所有生成的树中最小的那个呗!
太别扭了对吧?
好,我们来看看官方回答。
一个有 n 个结点的的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用(克鲁斯卡尔)算法或(普里姆)算法求出。
prim算法
Prim算法是通过先选取一个点,然后不断加入点的一个过程。
’初始化:V’={x},E’={},x是随便一个节点;
’重复下列操作,直到V’=V:
´在E集合当中选择最小的边<u,v>使得u∈V’但是v∉V’;
´V’加入节点v,E’加入<u,v>;
´(V’,E’)则为所求的最小生成树。
´我们可以对该算法里面的各个步骤分别考虑:
´初始化:V’={x},E’={},x是随便一个节点;
这一步只需要随便选取一个点即可;
´重复下列操作,直到V’=V:
´在E集合当中选择最小的边<u,v>使得u∈V’但是v∉V’;
´V’加入节点v,E’加入<u,v>;
对于上面的第二步,实际上我们只需要对于每一个点维护一个V’集合中的点到达该点的最短距离。
然后每次扫描一遍数组找到我们所需要的v加入V’;
复杂度为O(N^2+M).
对于上面的第二步操作,我们实际上可以通过堆(优先队列)维护一个满足u∈V’但是v∉V’的边集,那么我们就能迅速取出满足要求的边;
然后当改变了V’的时候,我们就可以根据新加入的节点v对原有的堆进行删除和插入操作。
需要注意的是,当我们用优先队列实现的时候,我们需要将删除操作延迟。
复杂度为O((N+M)logN).
哔哔了这么多,我们直接来看看代码吧! (当然先是一些板子)
#include<bits/stdc++.h>
using namespace std;const int maxn=10010;int n,m,edge[maxn][maxn];int dis[maxn];bool boo[maxn];int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;++i){ int x,y,z; scanf("%d%d%d",&x,&y,&z); edge[x][y]=edge[y][x]=z; } dis[1]=0; boo[1]=true; for(int i=2;i<=n;++i) dis[i]=123456789; for(int i=2;i<=n;++i) if(edge[1][i]) dis[i]=min(dis[i],edge[1][i]); int ans=0; for (int i=1;i<=n-1;++i) { int maxd=0; for(int j=1;j<=n;++j) { if(!boo[j]) if(maxd==0||dis[maxd]>dis[j]) maxd=j; } boo[maxd]=true; ans+=dis[maxd]; for(int j=1;j<=n;++j) { if(!boo[j]) if(edge[maxd][j]) dis[j]=min(dis[j],edge[maxd][j]); } } printf("%d\n",ans); return 0;} Kruskal算法
Kruskal算法主要分为两步:
给所有边按照边权从小到大的顺序排序;
从小到大依次考虑每条边(u,v)(最开始没有任何的边):
如果u与v已经连通了,那么加入(u,v)后会出现环,不添加;
如果u与v没有连通,那么加入(u,v)使其连通。
然后,对于判断是否联通,我们可以通过并查集来维护。
![](https://images2015.cnblogs.com/blog/1085703/201705/1085703-20170514210919097-1233542468.png)
是不是感觉k算法比p算法好理解啊(暂且先那么叫)
据徐大佬说k算法好像比p算法快,唉,如果让你写的话,是不是一定选择这种又快有好写还有好理解的代码啊。
反正让我写的的话,我一定会选择写k算法的,但是这仅限于裸地最小生成树的题。
对于有的题k算法是不如p算法的,甚至有的时候,你用k算法还不一定能做出来。
好了,废话不多说了,我们来看看代码吧。
#includeusing namespace std;const int maxn=100000+15;struct Edge{ int x,y,z;}edge[maxn];bool cmp(Edge a,Edge b){ return a.z
最小瓶颈生成树
有的童鞋看到这个就要抓狂了,这又是个什么鬼!!!
哎呀,不要着急,我们来具体看一看这个东西。
最小瓶颈生成树:在一个图中找出一棵树,使这棵树的最大权值最小。
给你一个这样的关系吧:最小生成树一定是最小瓶颈生成树,最小瓶颈生成树不一定是最小生成树。
最优比率生成树
给出一个图,每条边的权值是(a,b)的形式。求出一个生成树使得suma/sumb最小。
权值均为正值
´二分答案
´Suma/sumb>K
´Suma>Sumb*K,
´Sum(a-kb)>0
´一般的最大生成树
#includeusing namespace std;const int maxn=100000+15;struct Edge{ int x,y,a,b; Edge(int x=0,int y=0,int a=0,int b=0): x(x),y(y),a(a),b(b) {}}edge[maxn],edge2[maxn];int mst(){ return 0;}int change(int K){ for (int i=1;i<=m;i++) edge2[i]=Edge(edge[i].x,edge[i].y, edge[i].a-K*edge[i].b,0); return 0;}int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int x,y,a,b; scanf("%d%d%d%d",&x,&y,&a,&b); edge[i]=Edge(x,y,a,b); } int l=0,r=1000000,mid; while (l+1