前两天学完了数理统计,想再花四天时间把微观经济学给学完了,只是笔记本不知道够不够用啊啊啊QwQ
寒假结束前有一次集训队二招,不知道其他人准备的怎么样了。
今天复习的算法是最小生成树,最小生成树在运筹学和离散数学中都有提及,目前我印象中的算法有:避圈法、整树加边法,后来才发现其实这俩前者就是kruskal后者就是prim。。( ̄▽ ̄)"
1.Prim算法
数据结构:
二维矩阵g[][]存图;
lowcost[i]为第i个点到整棵树的距离,若没有路则为INF;
mst[i]表示以第i个点为终点,到整棵树距离最短的那条边对应的起点,即对应lowcost[i]的起点,当第i个点加入生成树后令其mst[i]=0。
主要思想:
整树加边法:任选一个起点作为树的初始化,加入一条离整棵树最近的边(点),再把整个树作为一个整体,找下一个离整棵树最近的边(点)加入树中,直到所有点都加入。
实现:
- 初始化:g[][]存图,默认第一个点为起点,lowcost[]均为到起点的距离(除起点为0),mst[]均为起点标号1;
- 操作——找:找出lowcost[]中最小非零点i(边);
if (lowcost[j] < min && lowcost[j] != 0){
min = lowcost[j];
minid = j;
}
- 操作——加入:加入最小生成树中即lowcost[i]=0,mst[i]=0;
- 操作——更新:不用重新扫一遍图!用刚加入的点比较一轮即可:
for (j = 2; j <= n; j++){
if (graph[minid][j] < lowcost[j]){
lowcost[j] = graph[minid][j];
mst[j] = minid;
}
}
- 跳出:当生成树中点数为n时即可跳出(循环n次以上三个操作步骤)。
这个博主图画的很好,数组名字我也是按他的取的:
https://blog.csdn.net/yeruby/article/details/38615045
2.kruskal算法
数据结构:
结构体数组a:边集数组,每个节点有三个数据存储一条边:起点a终点b权值
父节点数组par[i]:表示第i个点的父节点
并查集=父节点数组par[]+操作函数(建树操作,合并操作,找爸爸操作共三个基本函数)
主要思想:
避圈法,所谓避圈就是俩点不能属于同一集合。
- 初始化:将图看做一个森林,每个顶点为一棵独立的树,即一个独立的集合,边集a从题中读入,par数组初始值为本身标号;
- 操作——排序:将边集a按权值p从小到大排,自己写cmp函数:
int cmp(const void*a, const void *b){
return ((Node*)a)->price - ((Node*)b)->price;
}
然后是这样调用的,将数组a前n个元素按cmp的规定排好序:
qsort(a, n, sizeof(a[0]), cmp);
- 操作——加边(点):从小到大拿边,如果这条边的起点加边终点不在同一集合就合并(避圈加边法)
for(int i = 0; i < n && nEdge != m - 1; i++){
//判断当前这条边的两个端点是否属于同一棵树
if(find(a[i].a) != find(a[i].b)){
unite(a[i].a, a[i].b);//合并第i条边的起点和终点
res += a[i].price;
nEdge++;
}
}
- 跳出:自动跳出。注意:如果到最后生成树没达到n条边则说明这个图不存在最小生成树,因此在前面每加一个点都会让计数变量nedge自增一:
if(nEdge < m-1) res= -1;
return res;
好啦,今天就到这,我要出去耍啦~