有一张n个点m条边的有向图,还有一个包含k个点的点集,求出这个点集中任意两点间最短路的最小值
官方题解看不懂。。
如果一条边的两个端点都在这个集合中,就将这条边直接删掉(中间记录下最小值)以后不会再用
之后就可以愉快的SPFA了,len[i]表示到i点的最短路
随便设集合中的一个点x1为起点,令len[x1]=0然后求最短路,跑完之后记录所有的len[xi] (xi>1 && xi∈k)看哪个更小,之后再将集合中的第二个点x2的len[]置为0并加入队列,继续spfa,然后记录所有的len[xi] (xi>2 && xi∈k)看哪个更小,如此操作直到集合中所有点的len[]全为0
不过这样只处理了ai到aj (i<j)的最小值,所以还要初始化len[]倒过来再跑一次spfa,操作和上面一样
例如样例k=3,集合中的点为1,3,5
①删掉所有1,3,5之间的边,ans = min(ans, len(u, v)) (e(u, v)∈k)
②求出1号点到所有点的单源最短路, 然后ans = min(ans, len[3], len[5])
③令len[3]=0,将3号点加入队列继续spfa,ans = min(ans, len[5])
④没必要再加最后5号点了,初始化len数组
⑤求出5号点到所有点的单源最短路, 然后ans = min(ans, len[3], len[1])
⑥令len[3]=0,将3号点加入队列继续spfa,ans = min(ans, len[1])
结束,输出ans
看上去貌似是跑了2*k次spfa其实只跑了2次,因为你并没有清空len[]数组,所以只是相当于手动放缩了k次而已
复杂度O((n+m)log(n+m))
#include<stdio.h>
#include<vector>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define LL long long
typedef struct
{
LL v;
LL len;
}Road;
Road now;
vector<Road> G[100005];
queue<LL> q;
LL God[100005], vis[100005], sum[100005];
int main(void)
{
LL T, i, n, m, j, x, y, len, k, bet, cas = 1;
scanf("%lld", &T);
while(T--)
{
scanf("%lld%lld", &n, &m);
for(i=1;i<=n;i++)
G[i].clear();
for(i=1;i<=m;i++)
{
scanf("%lld%lld%lld", &x, &y, &len);
now.len = len, now.v = y;
G[x].push_back(now);
}
memset(God, 0, sizeof(God));
scanf("%lld", &k);
for(i=1;i<=k;i++)
{
scanf("%lld", &x);
God[x] = 1;
}
bet = 100000ll*100000+5;
for(i=1;i<=n;i++)
{
if(!God[i]) continue;
for(j=0;j<G[i].size();j++)
{
y = G[i][j].v;
if(God[y])
{
bet = min(bet, G[i][j].len);
G[i].erase(G[i].begin()+j);
j--;
}
}
}
memset(vis, 0, sizeof(vis));
memset(sum, 62, sizeof(sum));
for(i=1;i<=n;i++)
{
if(God[i])
{
vis[i] = 1;
q.push(i);
bet = min(bet, sum[i]);
sum[i] = 0;
while(q.empty()==0)
{
x = q.front();
q.pop();
vis[x] = 0;
for(j=0;j<G[x].size();j++)
{
now = G[x][j];
if(sum[x]+now.len<sum[now.v])
{
sum[now.v] = sum[x]+now.len;
if(vis[now.v]==0)
{
vis[now.v] = 1;
q.push(now.v);
}
}
}
}
}
}
memset(sum, 62, sizeof(sum));
for(i=n;i>=1;i--)
{
if(God[i])
{
vis[i] = 1;
q.push(i);
bet = min(bet, sum[i]);
sum[i] = 0;
while(q.empty()==0)
{
x = q.front();
q.pop();
vis[x] = 0;
for(j=0;j<G[x].size();j++)
{
now = G[x][j];
if(sum[x]+now.len<sum[now.v])
{
sum[now.v] = sum[x]+now.len;
if(vis[now.v]==0)
{
vis[now.v] = 1;
q.push(now.v);
}
}
}
}
}
}
printf("Case #%lld: %lld\n", cas++, bet);
}
return 0;
}