最短路(path)

本文详细解析了一道结合SPFA(Shortest Path Faster Algorithm)与DFS(Depth-First Search)的最短路径问题。通过具体实例,阐述了如何利用SPFA预处理各关键节点之间的最短路径,再运用DFS遍历所有可能的路径组合,最终求得从起点经由所有标记点到达终点的最短总距离。
摘要由CSDN通过智能技术生成

最短路(path) ⁡ \operatorname{最短路(path)} (path)

题目链接: SSL比赛 1141 ⁡ \operatorname{SSL比赛\ 1141} SSL 1141

题目

给定一个 n n n 个点 m m m 条边的有向图,有 k k k 个标记点,要求从规定的起点按任意顺序经过所有标记点到达规定的终点,问最短的距离是多少。

输入

第一行 5 5 5 个整数 n n n m m m k k k s s s t t t ,表示点个数、边条数、标记点个数、起点编号、终点编号。
接下来 m m m 行每行 3 3 3 个整数 x x x y y y z z z ,表示有一条从 x x x y y y 的长为 z z z 的有向边。
接下来 k k k 行每行一个整数表示标记点编号。

输出

输出一个整数,表示最短距离,若没有方案可行输出 − 1 -1 1

样例输入

3 3 2 1 1
1 2 1
2 3 1
3 1 1
2
3

样例输出

3

样例解释

路径为 1 → 2 → 3 → 1 1\rightarrow2\rightarrow3\rightarrow1 1231

数据范围

20 % 20\% 20% 的数据 n < = 10 n<=10 n<=10

50 % 50\% 50% 的数据 n < = 1000 n<=1000 n<=1000

另有 20 % 20\% 20% 的数据 k = 0 k=0 k=0

100 % 100\% 100% 的数据 n < = 50000 n<=50000 n<=50000 m < = 100000 m<=100000 m<=100000 0 < = k < = 10 0<=k<=10 0<=k<=10 1 < = z < = 5000 1<=z<=5000 1<=z<=5000

思路

这道题是一道 spfa 加 dfs 。

我们就直接以每个必走的点为起点跑 spfa (当然终点不用,后面讲是干嘛的),然后我们就其实得到了从一个必走点走到另外一个必走点的最短路径。
然后我们就可以直接暴搜,枚举走的路径,找到答案最小的那个。

这时候就有人问了:不会再从 a a a 必走点走到 b b b 必走点的时候经过了 c c c 必走点,从而使得路径变长了吗?
其实不会,因为如果中途进过了另一个必走点,我们完全可以先从 a a a 走到 c c c ,再从 c c c 走到 b b b ,其实是一样的,因为都是最短路。

然后这道题最烦的地方则是有关 long long 和初始化。
首先,要用 long long 。
(我90分的时候卡了贼久,听说要用 long long 就改了,但是一直没改输出的 %d 。最惨的是我下载不了数据,一直以为是算法的问题。然后就卡了半个小时。。。)
(在此感谢 hky 大爷赠送的数据,顺膜%%%)

而且初始化的啥时候要赋值大一点,然后初始化两点距离的时候就要直接 for 循环来初始化。

代码

#include<queue>
#include<cstdio>
#include<cstring>
#define ll long long
#define tmp 1e18

using namespace std;

struct node {
	ll x, to, nxt;
}e[100001];
ll n, m, k, s, t, le[50001], KK;
ll x, y, z, must[13], dis[13][50001];
ll ans;
bool in[50001];

void add(ll x, ll y, ll z) {//邻接表
	e[++KK] = (node){z, y, le[x]}; le[x] = KK;
}

ll read() {//快读
	ll an = 0, zhengfu = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') zhengfu = -zhengfu;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		an = an * 10 + c - '0';
		c = getchar();
	}
	return an * zhengfu;
}

void spfa(ll ss, ll num) {//ss为起点的spfa
	for (int i = 0; i <= 50000; i++)
		dis[num][i] = tmp;
	memset(in, 0, sizeof(in));
	in[ss] = 1;
	queue <ll> q;
	q.push(ss);
	dis[num][ss] = 0;
	
	while (!q.empty()) {
		ll now = q.front();
		q.pop();
		
		for (ll i = le[now]; ~i; i = e[i].nxt)
			if (dis[num][e[i].to] > dis[num][now] + e[i].x) {
				dis[num][e[i].to] = dis[num][now] + e[i].x;
				if (!in[e[i].to]) {
					in[e[i].to] = 1;
					q.push(e[i].to);
				}
			}
		
		in[now] = 0;
	}
}

void dfs(ll dep, ll sum, ll fa) {//暴力dfs搜
	if (dep == k) {
		ans = min(ans, sum + dis[fa][t]);
		return ;
	}
	
	for (ll i = 1; i <= k; i++)
		if (!in[i]) {
			in[i] = 1;
			sum += dis[fa][must[i]];
			dfs(dep + 1, sum, i);
			sum -= dis[fa][must[i]];
			in[i] = 0;
		}
}

int main() {
	memset(le, -1, sizeof(le));//初始化
	
	n = read();//读入
	m = read();
	k = read();
	s = read();
	t = read();
	for (ll i = 1; i <= m; i++) {
		x = read();//读入
		y = read();
		z = read();
		add(x, y, z);//建图
	}
	
	spfa(s, 0);//每个必走的点走为起点跑spfa(终点不用)
	for (ll i = 1; i <= k; i++) {
		must[i] = read();
		spfa(must[i], i);
	}
	
	ans = tmp;//dfs暴搜答案
	dfs(0, 0, 0);
	
	if (ans >= tmp) printf("-1");//输出
		else printf("%lld", ans);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值