通过POJ 3463 Sightseeing(dijkstra)彻底理解优先队列优化的dijkstra算法

先仔细梳理一下优先队列优化的dijkstra算法思路:

我们要定义一个结构体,包含终点v,和从源点到终点v的最短路径长度d

struct node {
	int v, d;
	node(){}
	node(int v, int d) :v(v), d(d) {}
	//由于优先队列的特点重载<操作符,使得d最小的排在队列的front
	bool operator<(const node&op)const {
		return d > op.d;
	}
};

然后最重要的地方就是:我们每次从优先队列里取出的元素如果点v没被标记,那么从源点到点v的最短路就是d了。注意优先队列里对于一个顶点v可能有好多node对应着不同的d,但是每次我们最先取出来的是d最小的,如果此时v没被标记过的话,我们就找到了v的最短路,但是如果此时v已经标记过了,那么我们就已经找过了v的最短路,此时找的d是>=最短路的,所以我们就不用处理这个node。

下面是完整优先队列优化的dijkstra代码:

struct node {
	int v, d;
	node(){}
	node(int v, int d) :v(v), d(d) {}
	//由于优先队列的特点重载<操作符,使得d最小的排在队列的front
	bool operator<(const node&op)const {
		return d > op.d;
	}
};

int fst[N], nxt[M], to[M], cost[M], e;
int dis[N];
bool vis[N];//vis[u]表示我们得到u到源点s的最短路之前为false,之后为true

void add(int u, int v, int c) {
	to[e] = v;
	cost[e] = c;
	nxt[e] = fst[u];
	fst[u] = e++;
}

void dijkstra(int s) {
	memset(dis, 0x3f, sizeof dis);
	memset(vis, false, sizeof vis);
	dis[s] = 0;
	priority_queue<node>q;
	q.push(node(s, 0));
	while (!q.empty()) {
		node now = q.top(); q.pop();
		int u = now.v;
		if (vis[u])continue;//在这之前已经找到了点u的最短路
		vis[u] = true;//此时找到了到点u的最短路
		//dis[u] = now.d;//这句有没有无所谓
		for (int i = fst[u]; i != -1; i = nxt[i]) {
			int v = to[i], c = cost[i];
			if (dis[v] > now.d + c) {
				dis[v] = now.d + c;
				q.push(node(v, dis[v]));
			}
		}
	}
}

最后来说一下poj 3463这个题,题目让求最短路和比最短路长1的路径个数。考虑dijkstra算法的话,把dis和vis数组都换成二维的dis[N][2],vis[N][2],分别表示最短路和次短路,然后用cnt[N][2]来分别记录最短路和次短路的路径个数。然后用dijkstra算法优先队列优化一下就OK了。注意对于取出来的元素我们要知道他是最短路还是次短路,所以结构体要加一个变量表示最短还是次短。

稍微复杂一点的地方就是更新最短路或次短路条件的判断,要分四种情况。

下面是ac代码:

#pragma warning(disable:4996)
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1005;
const int M = 10005;

int fst[N], nxt[M], to[M], cost[M], e;
int S, F, n, m;
int dis[N][2], cnt[N][2];
bool vis[N][2];

struct node {
	int type;//标记最短路->0,还是次短路->1
	int v;//标记终点
	int len;//标记S->v路径长度
	node(){}
	node(int type, int v, int len) :type(type), v(v), len(len) {}
	bool operator<(const node&op)const {
		return len > op.len;
	}
};
priority_queue<node>q;

void init() {
	while (!q.empty())q.pop();
	e = 0;
	memset(fst, -1, sizeof fst);
}

void add(int u, int v, int c) {
	to[e] = v;
	cost[e] = c;
	nxt[e] = fst[u];
	fst[u] = e++;
}

int dijkstra() {
	memset(dis, 0x3f, sizeof dis);
	memset(vis, false, sizeof vis);
	memset(cnt, 0, sizeof cnt);
	dis[S][0] = 0;
	//vis[S][0] = true;
	cnt[S][0] = 1;
	q.push(node(0, S, 0));
	while (!q.empty()) {
		node now = q.top(); q.pop();
		if (vis[now.v][now.type])continue;//找到下一个未访问的node
		vis[now.v][now.type] = true;//此时最短路或次短路标记访问完成
		int u = now.v;
		for (int i = fst[u]; i != -1; i = nxt[i]) {
			int v = to[i], c = cost[i];
			int d = now.len + c;
			if (d < dis[v][0]) {//此时最短路,次短路都换
				dis[v][1] = dis[v][0];
				cnt[v][1] = cnt[v][0];
				dis[v][0] = d;
				cnt[v][0] = cnt[u][now.type];
				q.push(node(0, v, dis[v][0]));
				q.push(node(1, v, dis[v][1]));
			}
			else if (d == dis[v][0]) {//此时最短路条数增加(不只+1)
				cnt[v][0] += cnt[u][now.type];;
			}
			else if (d < dis[v][1]) {//此时次短路换
				dis[v][1] = d;
				cnt[v][1] = cnt[u][now.type];
				q.push(node(1, v, dis[v][1]));
			}
			else if (d == dis[v][1]) {//此时次短路条数增加
				cnt[v][1]+= cnt[u][now.type];
			}
		}
	}
	if (dis[F][1] - dis[F][0] <= 1)return cnt[F][1] + cnt[F][0];
	return cnt[F][0];
}

int main() {
	//freopen("in.txt", "r", stdin);
	int t; scanf("%d", &t);
	while (t--) {
		init();
		scanf("%d %d", &n, &m);
		for (int i = 1; i <= m; i++) {
			int u, v, c; scanf("%d %d %d", &u, &v, &c);
			add(u, v, c);
		}
		scanf("%d %d", &S, &F);
		printf("%d\n", dijkstra());
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值