【链接】:http://hihocoder.com/problemset/problem/1109
小Hi这时暂时完成了今天的游戏日常,回过头来对小Ho道:“其实主要是Prim算法本身还有很多需要优化的地方,比如用堆就可以很好的解决求最小值部分和合并节点的问题。”。
小Ho想了想,道:“堆?求最小值的问题……让我想想,每次将1号节点与一个新的节点K合并的时候,等于是将原来节点K相连的边统统改连到1号节点上,也就是1号节点新添加了几条边,在稀疏图的前提下,这样的边数其实不是很多(或者说总和为O(M)级别的),但是在处理的过程中,由于不是针对稀疏图进行的优化,所以就没有很好的处理这一点(因为如果不是稀疏图的话O(N^2)和O(M)其实是一样的),而是每次都在与1号点相连的所有边中选取最短的边……所以说用堆优化的方法,其实就是用小根堆维护一个与1号节点相连的边的集合,然后每次在其中找出最小的边,而将这条边连接的点加入到1号节点中,其实就是用新加入节点连接出的几条边去更新堆。”
小Hi点了点头:“对的,这就是为什么Prim算法在稀疏图上比Kruscal要慢的原因——本身就存在可以优化的地方,那么你不妨思考一下,现在的时间复杂度是怎么样的呢?”
小Ho道:“Prim算法本身的时间复杂度是O(N^2)的,而在这个算法中,使用了堆来维护所有的边,操作数一共是O(M)级别的,所以复杂度是O(MlogM)的!而Kruscal算法的时间复杂度是O(MlogM + M * Ackermann’(M)),总的说来,是优化到了和Kruscal算法同样级别了呢。”
“没错!”小Hi笑道。
代码:
/***********************
最小生成树 prim priority_queue优化
Author:herongwei
Time:2017/2/28 10:30
language:C++
http://blog.csdn.net/u013050857
***********************/
#pragma comment(linker,"/STACK:102400000,102400000")
#include <bits/stdc++.h>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
#include <deque>
#include <set>
#include <map>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn =1e5+10;
const int inf =0x3f3f3f3f;
const LL MOD = 999999997;
int dir4[4][2]= {{1,0},{0,1},{-1,0},{0,-1}};
int dir8[8][2]= {{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
struct edge /*构建小顶堆*/
{
int to;
int dis;
bool operator < (const edge& t) const{
return dis>t.dis;
}
};
priority_queue<edge> q; /*priority_queue q */
vector<edge> G[maxn]; /*graph*/
bool vis[maxn]; /*vis标记数组是否访问*/
int N,M,a,b,w; /*vertex edge*/
void Init() /*初始化*/
{
while(!q.empty()) q.pop();
for(int i=0; i<maxn; ++i) G[i].clear();
memset(vis,false,sizeof(vis));
}
void input() /*a输入,b点的权值*/
{
for(int i=0; i<M; ++i){
scanf("%d%d%d",&a,&b,&w);
G[a].push_back(edge{b,w});
G[b].push_back(edge{a,w});
}
}
int priority_queue_prim()
{
/*维护一个与1号节点相连的边的集合,然后每次在其中找出最小的边*/
for(int i=0; i<G[1].size(); ++i){
q.push(G[1][i]);
}
vis[1]=true; /*第一个点标记访问*/
int ret=0; /*统计权值*/
int cnt=N-1; /*所有点是否访问*/
while(!q.empty() &&cnt)
{
edge p=q.top(); q.pop();
if(vis[p.to]) continue;
ret+=p.dis;
cnt--;
vis[p.to]=true; /*将这条边连接的点加入到1号节点中,然后用新加入节点连接出的几条边去更新优先队列*/
for(int i=0; i<G[p.to].size(); ++i){
edge pp=G[p.to][i];
if(!vis[pp.to]) q.push(pp);
}
}
printf("%d\n",ret);
}
int main()
{
// freopen("in.txt", "r", stdin);
while(~scanf("%d %d", &N, &M)){
Init();
input();
priority_queue_prim();
}
return 0;
}
//http://hihocoder.com/problemset/problem/1109
【result】1109 最小生成树三·堆优化的Prim算法 AC G++ 643ms 27MB 38秒前 查看