一个 n n 个点 m m 条边的有向图,有 k k 个标记点,要求从规定的起点 S S 按任意顺序经过所有标记点到达规定的终点 T T ,问最短的距离是多少。
n<=50000,m<=100000 n <= 50000 , m <= 100000
0<=k<=10,1<=边权<=5000 0 <= k <= 10 , 1 <= 边 权 <= 5000
对于起点 S S 跟每个标记点进行一遍 spfa s p f a ,
然后我们考虑经过的标记点的顺序,这个利用 dfs d f s 去枚举,有 k! k ! 种情况,
如果需要经过的标记点为 x,y x , y ,
那么显然路径有可能是
S−>x−>y−>T S − > x − > y − > T 或者 S−>y−>x−>T S − > y − > x − > T , −> − > 表示间接或者直接地到达,
那么最小距离显然是
min(dis[S][x]+dis[x][y]+dis[y][T],dis[S][y]+dis[y][x]+dis[x][T]) m i n ( d i s [ S ] [ x ] + d i s [ x ] [ y ] + d i s [ y ] [ T ] , d i s [ S ] [ y ] + d i s [ y ] [ x ] + d i s [ x ] [ T ] )
因为假设我们 S−>T S − > T 的路径上必须要经过标记点 x x ,那么分成 S−>x S − > x 的最短路+ x−>T x − > T 的最短路,必定最优。
#include
#include
#include
#include
#include
#include
#define N 50005
using namespace std;
typedef long long ll;
struct Node { int To, nxt; ll w; }e[2*N];
int vis[N], ls[N], Chose[15], C[15];
int n, m, k, s, t, cnt, tot;
ll dis[15][N], ans = -1;
queue <int> Q;
void Addedge(int u, int v, ll w)
{
e[++cnt].To = v, e[cnt].w = w, e[cnt].nxt = ls[u], ls[u] = cnt;
}
void spfa(int x)
{
++tot;
for (int i = 1; i <= n; i++)
vis[i] = 0, dis[tot][i] = -1;
dis[tot][x] = 0;
vis[x] = 1;
while (Q.size()) Q.pop();
Q.push(x);
while (Q.size())
{
int u = Q.front();
Q.pop();
for (int i = ls[u]; i; i = e[i].nxt)
{
int v = e[i].To;
if (dis[tot][u] + e[i].w < dis[tot][v] || dis[tot][v] == -1)
{
dis[tot][v] = dis[tot][u] + e[i].w;
if (!vis[v])
{
vis[v] = 1;
Q.push(v);
}
}
}
vis[u] = 0;
}
}
void dfs(int dep, ll now, int x)
{
if (dep == k)
{
if (now + dis[x][t] < ans || ans == -1) ans = now + dis[x][t];
return;
}
if (dis[x][t] == -1) return;
if (now + dis[x][t] >= ans && ans != -1) return;
for (int i = 1; i <= k; i++)
if (!Chose[i])
{
Chose[i] = 1;
dfs(dep + 1, now + dis[x][C[i]], i);
Chose[i] = 0;
}
}
int main()
{
scanf("%d %d %d %d %d", &n, &m, &k, &s, &t);
for (int i = 1; i <= m; i++)
{
int u, v; ll w;
scanf("%d %d %lld", &u, &v, &w);
Addedge(u, v, w);
}
tot = -1;
spfa(s);
if (k == 0)
{
if (dis[0][t] == -1) printf("-1\n");
else printf("%lld\n", dis[0][t]);
return 0;
}
for (int i = 1; i <= k; i++)
{
scanf("%d", &C[i]);
spfa(C[i]);
}
for (int i = 1; i <= k; i++)
{
Chose[i] = 1;
dfs(1, dis[0][C[i]], i);
Chose[i] = 0;
}
if (ans == -1) printf("-1\n");
else printf("%lld\n", ans);
return 0;
}