D e s c r i p t i o n Description Description
给定一个n个点m条边的有向图,有k个标记点,要求从规定的起点按任意顺序经过所有标记点到达规定的终点,问最短的距离是多少。
I n p u t Input Input
第一行5个整数n、m、k、s、t,表示点个数、边条数、标记点个数、起点编号、终点编号。
接下来m行每行3个整数x、y、z,表示有一条从x到y的长为z的有向边。
接下来k行每行一个整数表示标记点编号。
O u t p u t Output Output
输出一个整数,表示最短距离,若没有方案可行输出-1。
S a m p l e Sample Sample I n p u t Input Input
3 3 2 1 1
1 2 1
2 3 1
3 1 1
2
3
S a m p l e Sample Sample O u t p u t Output Output
3
H i n t Hint Hint
路径为1->2->3->1。
20%的数据n<=10。
50%的数据n<=1000。
另有20%的数据k=0。
100%的数据n<=50000,m<=100000,0<=k<=10,1<=z<=5000。
T r a i n Train Train o f of of T h o u g h t Thought Thought
看到数据k很小
就知道是暴力了
我们可以先k+1个SPFA来算这k个特殊点和起始点的最短路
然后暴力全排列k个点的顺序
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;
int bol[50250], A[20], B[50250], H[50250], F[20];
int n, m, t, tot, start, end, x, y, z, g;
ll dis[20][50250];
ll Ans;
struct wh_
{
int w, h, k;
}wh[100250];
void hw(int x, int y, int z)
{
wh[++tot] = (wh_){
y, H[x], z}; H[x] = tot;}
void SPFA(int l, int x)
{
dis[l][x] = 0;
queue<int>hy;
hy.push(x);
bol[x] = 0;
while(hy.size())
{
int h = hy.front(); hy.pop();
for(int i = H[h]; i; i = wh[i].h)
if(dis[l][h] + wh[i].k < dis[l][wh[i].w])
{
dis[l][wh[i].w] = dis[l][h] + wh[i].k;
if(!bol[wh[i].w])
{
hy.push(wh[i].w);
bol[wh[i].w] = 1;
}
}
bol[h] = 0;
}
}
void Dfs(int now, ll sum)
{
if(sum > Ans)return;//剪枝
if(now == t)
{
sum += dis[B[F[now]]][end];
Ans = min(Ans, sum);
return;
}
for(int i = 1; i <= t; ++i)
if(!bol[A[i]])
{
bol[A[i]] = 1;
F[now + 1] = A[i];
Dfs(now + 1, sum + dis[B[F[now]]][A[i]]);
bol[A[i]] = 0;
}
return;
}
int main()
{
memset(dis, 0x7f, sizeof(dis));
scanf("%d%d%d%d%d", &n, &m, &t, &start, &end);
for(int i = 1; i <= m; ++i)
{
scanf("%d%d%d", &x, &y, &z);
hw(x, y, z);//ps:有向边
}
g = 0;
SPFA(0, start);//起始点SPFA
ll MAX = dis[19][50200];
if(dis[0][end] != MAX)g = 1;//判断是否可以联通终点
for(int i = 1; i <= t; ++i)
{
scanf("%d", &A[i]);
B[A[i]] = i;
SPFA(i, A[i]);
if(dis[i][end] != MAX)g = 1;
}
// printf(" ");
// for(int i = 1; i <= n; ++i)
// printf(" %d", i);
// printf("\n%d", start);
// for(int i = 1; i <= n; ++i)
// printf(" %d", dis[0][i]);
// for(int i = 1; i <= t; ++i)
// {
// printf("\n%d", A[i]);
// for(int j = 1; j <= n; ++j)
// printf(" %d", dis[i][j]);
// }
if(!g)
{
printf("-1");
return 0;
}
F[0] = start, Ans = 1e15;//Ans要弄大一点,否则90分
Dfs(0, 0);//全排列
printf("%lld", Ans);
return 0;
}