在正常求次小生成树的时候相信大家都喜欢kruskal,毕竟因为太大的图,尤其是稀疏图,prim算法并不实用,而且占内存太多,有可能不让开。但是在解决次小生成树和限制度生成树时候prim算法不失为一种很好的方法,因为这两种情况空间复杂度,尤其是图的大小都并不太大。
从一开始学了prim算法,再到拿prim算法解决次小生成树的变形,直到今天彻底搞懂prim算法求解限制k度最小生成树的问题。我感觉无论如何边,唯一不变的一点就是:
把贪心贯彻到底!!!
最小生成树:朴素prim 与dijkstra相同,一直找最小,一直更新插入
次小生成树:在求解完最小生成树后,找一个未被使用的最小边进行判断
限制K度生成树: 在分块求解完最小生成树后,进行每一次从所有树中找一个差值最大的,重新建树,松弛更新
一:
先说一下普通的最小生成树的题,prim算法 的不优化和优化的两种算法。
1.不优化-详细请点http://blog.csdn.net/qq_33951440/article/details/52640783
以POJ 1258 Agri-Net题为例
void prim(int cur)
{
int sum=0;
for(int i=1;i<=n;i++)
{
dis[i]=mp[cur][i];
}
vis[cur]=1;
for(int i=1;i=mp[index][j])
{
dis[j]=mp[index][j];
}
}
}
}
之后就是优化后的prim算法了,用优先队列来搞定,复杂度降低成为了(nlogn),然而空间复杂度仍然是一个问题
priority_queueq;
while(!q.empty())
q.pop();
node fir;
fir.v=cur,fir.val=0;
q.push(fir);
while(!q.empty())
{
node tp=q.top();
q.pop();
int u=tp.v;
if(!tree[u])
{
ans+=tp.val;
tree[u]=id;
for(int i=1;imp[u][i])
{
pre[i]=u;
dis[i]=mp[u][i];
node temp;
temp.val=dis[i];
temp.v=i;
q.push(temp);
}
}
}
}
return ans;
题意:在最小生成树的基础上,询问是否可以不经过最小生成树构造时候经过的边,重新更改一条或者多条边,使得权值和仍然与最小生成树的权值和相同。
思路:在构造最小生成树时候,把用到的边进行全部标记掉,然后再从未被标记的边中找出一条边进行更改。
注意:这个时候prim算法就会看出好处了,在空间允许的情况下我们可以直接用二维数组 类似于mp[i][j]=0 的形式直接把边标记点,而不非要同kruskal算法一样重新构造
具体的点击
三.限制K度最小生成树
题意:在普通最小生成树的基础上,题意允许你再多加K条边,构造一个 K度的最小生成树。
大体意思就是:
当把最小生成树都构造好(注意有可能不只是一棵树)之后再进行加入m条边,或者少于m条边,使得权值更小。我不感觉我可以讲理论讲得很好,所以具体的请看大神写得国家队论文,http://wenku.baidu.com/view/8abefb175f0e7cd1842536aa.html
思路:在不加入总目标的那个点之前,把所有的点都尽可能能连起来,也就是构造尽量少的最小生成树,之后再把每一棵树中找到一个最短的边,把树与总目标点相连,如果还有多余的度可以让我们插入,我们就在所有点中找到一个可以松弛最大的,注意不是最小的,而是相比之下松弛更大的。
我在这里只是把代码一步步的注释,记录下自己一步步调试的发现Orz
以题POJ1639为例
#include
#include
#include
#include