最短路(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 1→2→3→1 。
数据范围
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;
}