堆优化的迪杰斯特拉算法 - 社交网络图中结点的“重要性”计算

这是一道来自PAT的算法与数据结构的练习题。原题链接:7-36 社交网络图中结点的“重要性”计算

借这道题讲讲堆优化的迪杰斯特拉算法怎么写。

首先解读下题目,题目很长啊,不过有用的话就一句:结点vi的“紧密度中心性”Cc(vi)数学上定义为vi到其余所有结点vj(ji) 的最短距离d(vi,vj)的平均值的倒数。因此,这是个最短路问题,而且图中可能存在环。

数据规模:点N<104,边M<105,询问次数K<102。弗洛伊德算法(时间复杂度O(n3))不用考虑,而求单源最短路径的算法就剩下迪杰斯特拉、BellmanSPFA,而BellmanSPFA的复杂度和边的规模有关,这题中的边比点多,因此不宜使用,故只能使用迪杰斯特拉算法了。

而迪杰斯特拉算法的时间复杂度为O(n2)n是点数。如果直接使用,总的复杂度为O(kn2)达到了1010,显然是不能接受的,因此只能用堆去优化迪杰斯特拉算法。

普通迪杰斯特拉算法的思想需要明白,不明白的可以看我之前的博客Dijkstra或者网上其他人的博客。

给出普通迪杰斯特拉的算法代码:

#define Inf 0xfffffff    //无穷大
#define Maxn 1005

int map[Maxn][Maxn];    //邻接矩阵
int dis[Maxn];            //最短路径
int visit[Maxn];        //是否求得最短路径

void dij(int tag)
{
    //Init
    memset(visit,0,sizeof(visit));
    int i;
    for(i=1;i<=n;i++)
        dis[i]=map[tag][i];
    visit[tag]=1;
    //一个点一个点,直到visit都为1
    for(i=1;i<n;i++)
    {
        int temp=Inf;
        int k=tag;
        int j;
        //找最短的
        for(j=1;j<=n;j++)
            if(temp>dis[j]&&!visit[j])
                temp=dis[j],k=j;
        visit[k]=1;
        //松弛
        for(j=1;j<=n;j++)
            if(dis[j]>dis[k]+map[k][j]&&!visit[j])
                dis[j]=dis[k]+map[k][j];
    }
}

可以看出找最短的dis[j]和松弛这两个操作时间复杂度为O(n),这里把查找最短的dis[j]用堆去实现,可以优化到O(logn)

堆优化的迪杰斯特拉算法,采用优先队列的bfs实现,每次都队列中找到最短的dis,用对应结点去松弛其他点,把成功松弛过的点又加入到队列,让这些点成为候选的最短dis

由于对领接表存储的图进行bfs的时间复杂度为O(n+e),而优先队列弹出队头元素的时间复杂度为O(logn),由于每一个点都要被弹出队列一次,故堆优化的迪杰斯特拉的时间复杂度为O(nlogn)

对于这个题目来说,堆优化过后总的时间复杂度为O(knlogn)达到了106,在2s的时限内是可以接受的。

下面是堆优化迪杰斯特拉算法的模板

void dijstra(int src)
{
    vector<bool> used(n + 1, false);
    d.clear();
    d.resize(n + 1, INF);
    d[src] = 0;
    priority_queue<Node> que;
    que.emplace(src, d[src]);
    while (!que.empty()) {
        auto now = que.top();
        que.pop();
        used[now.id] = true;
        d[now.id] = now.val;
        for (int i = head[now.id]; i + 1; i = edges[i].next)
            if (!used[edges[i].to] && d[edges[i].to] > d[now.id] + 1)
                d[edges[i].to] = d[now.id] + 1,
                que.emplace(edges[i].to, d[edges[i].to]);
    }
}

最后这道题的完整代码:

#include <bits/stdc++.h>

using namespace std;

typedef struct tagEdge Edge;

const int INF = 0x3f3f3f30;

struct tagEdge {
    int to, next;
    tagEdge() {}
    tagEdge(int to, int next) : to(to), next(next) {}
};

struct Node {
    int id, val;
    Node() {}
    Node(int id, int val) : id(id), val(val) {}
    bool operator < (const Node &other) const {
        return val > other.val;
    }
};

struct Solution {
    int n, m;
    vector<int> head;
    vector<Edge> edges;
    vector<int> d;

    vector<int> question;

    Solution(int n, int m);
    void addEdge(int x, int y, int i);
    void dijstra(int src);
    void work();
};

void Solution::addEdge(int x, int y, int i)
{
    edges.emplace_back(y, head[x]);
    head[x] = i;
}

Solution::Solution(int n, int m)
{
    this->n = n;
    this->m = m;
    head.resize(n + 1, -1);
    for (int i = 0; i < 2 * m; i += 2) {
        int x, y;
        cin >> x >> y;
        addEdge(x, y, i);
        addEdge(y, x, i + 1);
    }
    int k, x;
    for (cin >> k; k; k--)
        question.push_back((cin >> x, x));
    d.resize(n + 1, INF);
}

void Solution::dijstra(int src)
{
    vector<bool> used(n + 1, false);
    d.clear();
    d.resize(n + 1, INF);
    d[src] = 0;
    priority_queue<Node> que;
    que.emplace(src, d[src]);
    while (!que.empty()) {
        auto now = que.top();
        que.pop();
        used[now.id] = true;
        d[now.id] = now.val;
        for (int i = head[now.id]; i + 1; i = edges[i].next)
            if (!used[edges[i].to] && d[edges[i].to] > d[now.id] + 1)
                d[edges[i].to] = d[now.id] + 1,
                que.emplace(edges[i].to, d[edges[i].to]);
    }
}

void Solution::work()
{
    dijstra(1);
    bool f = true;
    for (int i = 1; i <= n; i++)
        if (d[i] == INF) {
            f = false;
            break;
        }
    if (!f) {
        for (auto ele : question)
            printf("Cc(%d)=0.00\n", ele);
        return ;
    }
    for (auto ele : question) {
        dijstra(ele);
        double ans = 0;
        for (int i = 1; i <= n; i++)
            ans += d[i];
        ans = (double)(n - 1) / ans;
        printf("Cc(%d)=%.2f\n", ele, ans);
    }
}

int main()
{
    int n, m;
    while (cin >> n >> m) {
        Solution solution(n, m);
        solution.work();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值