最小生成树Prim算法的priority_queue优化

【链接】: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秒前 查看

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值