Sightseeing HDU - 1688(最短路 + 次最短路)

题意:给你一个有向带权图, 让你求出最短路条数, 如果次最短路的距离 = 最短路 + 1, 那么结果再加上次最短路的条数
板子题:

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
int inf = 0x3f3f3f3f;
int h[N], e[N<<2], ne[N<<2], f[N<<2], cnt;
void add(int u, int v, int w)
{
	e[cnt] = v, f[cnt] = w, ne[cnt] = h[u], h[u] = cnt ++;
}

int dis[N][2], num[N][2]; // o 表示最短路 1表示次短路路
int vis[N][2];// 0表示最短路是否走过, 1表示次短路是否走过
int n, m;
void init()
{
	cnt = 0;
	for(int i = 1; i <= n; i ++)
	{
		dis[i][0] = dis[i][1] = inf;
		vis[i][0] = vis[i][1] = 0;
		num[i][0] = num[i][1] = 0;
		h[i] = -1;
	}
}

struct node
{
	int u, w, id;
	bool operator < (const node &t) const
	{
		return t.w < w;
	}
};

void dijkstra(int s)
{
	dis[s][0] = 0;
	num[s][0] = 1;
	priority_queue<node> q;
	q.push({s, 0, 0});

	while(!q.empty())
	{
		node t = q.top();
		q.pop();
		int w = t.w, id = t.id;
		int x = t.u;
		if(vis[x][id]) continue;//同求最短路时相似,已经找到的最(次)短路,无需重复更新
		vis[x][id] = 1;//标记已经找到的最优解

		for(int i = h[x]; ~i; i = ne[i])
		{
			int u = e[i];
			int temp = w + f[i];
			if(dis[u][0] > temp)//借助这条边,缩短to和起点的最短距离,同时更新次最短路
			{
				dis[u][1] = dis[u][0];//之前存在最短路,这样才可以将原最短路当作次短路
				num[u][1] = num[u][0];
				q.push({u, dis[u][1], 1});//新的次最短路,放进优先队列,看是否可以来更新其他次短路
				dis[u][0] = temp;
				num[u][0] = num[x][0];借助node.x和to之间的边得到的新的最短路
				q.push({u, dis[u][0], 0});
			}
			else if(dis[u][0] == temp)//从node.x到to的边形成同样的最短路
			{
				num[u][0] += num[x][0];
			}
			else if(dis[u][1] > temp)//借助这条边,缩短to和起点的次最短距离
			{
				dis[u][1] = temp;
				num[u][1] = num[x][id];
				q.push({u, dis[u][1], 1}); // 新的次最短路,放进优先队列,看是否可以来更新其他次短路
			}
			else if(dis[u][1] == temp)
			{
				num[u][1] += num[x][id];//从node.x到to的边形成同样的次最短路
			}
		}
	}
}
int main()
{
	int t;
	scanf("%d", &t);
	while(t --)
	{
		scanf("%d%d", &n, &m);
		init();
		for(int i = 1; i <= m; i ++)
		{
			int x, y, z;
			scanf("%d%d%d", &x, &y, &z);
			add(x, y, z);
		}
		int s, en;
		scanf("%d%d", &s, &en);
		dijkstra(s);
		int sum = num[en][0];
		if(dis[en][0] == dis[en][1] - 1)
		{
			sum += num[en][1];
		}
		printf("%d\n", sum);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值