这是一道来自PAT的算法与数据结构的练习题。原题链接:7-36 社交网络图中结点的“重要性”计算。
借这道题讲讲堆优化的迪杰斯特拉算法怎么写。
首先解读下题目,题目很长啊,不过有用的话就一句:结点 vi 的“紧密度中心性” Cc(vi) 数学上定义为 vi 到其余所有结点 vj(j≠i) 的最短距离 d(vi,vj) 的平均值的倒数。因此,这是个最短路问题,而且图中可能存在环。
数据规模:点 N<104 ,边 M<105 ,询问次数 K<102 。弗洛伊德算法(时间复杂度 O(n3) )不用考虑,而求单源最短路径的算法就剩下迪杰斯特拉、 Bellman 、 SPFA ,而 Bellman 、 SPFA 的复杂度和边的规模有关,这题中的边比点多,因此不宜使用,故只能使用迪杰斯特拉算法了。
而迪杰斯特拉算法的时间复杂度为 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;iint 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 used(n + 1, false);
d.clear();
d.resize(n + 1, INF);
d[src] = 0;
priority_queue 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
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 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 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;
}