题意
一个含有
n
n
n个结点
m
m
m条边的无向有权图,判断每个结点是否在
1
1
1到
n
n
n的最短路径上。
如果该结点不可能出现在从
1
1
1到
n
n
n的最短路径上,输出
0
0
0。
如果该结点出现在所有从
1
1
1到
n
n
n的最短路径上,输出
1
1
1。
如果该结点出现在部分从
1
1
1到
n
n
n的最短路径上,输出
2
2
2。
数据范围
1
≤
T
≤
1000
1 \leq T \leq 1000
1≤T≤1000
2
≤
n
≤
1000
2 \leq n \leq 1000
2≤n≤1000
n
−
1
≤
m
≤
2
∗
1
0
5
n - 1 \leq m \leq 2*10^5
n−1≤m≤2∗105
1
≤
w
i
≤
1
0
8
1 \leq w_i \leq 10^8
1≤wi≤108
思路
首先判断这个点有没有可能出现在最短路上,判断方法是,用两个数组
d
i
s
t
1
,
d
i
s
t
n
dist1,distn
dist1,distn分别记录从
1
1
1到其他点的最短路,从
n
n
n到其他点的最短路。如果
d
i
s
t
1
[
i
]
+
d
i
s
t
n
[
i
]
=
d
i
s
t
1
[
n
]
dist1[i] + distn[i] = dist1[n]
dist1[i]+distn[i]=dist1[n],那么该点可能出现在最短路上,否则不可能。
下面判断该点会不会出现在所有最短路径上。这个思路比较巧妙,就是再建一个无向图,边为所有从
1
1
1到
n
n
n最短路上的边。怎么求最短路上的边呢,就是如果两个端点都是最短路上的点,那么这条边就是最短路上的边。然后求割点,如果这个点为割点,那么它就一定会出现在最短路上,否则就只会在部分最短路中出现。
这里需要特判一下
1
1
1号点和
n
n
n号点,这两个点一定会出现在所有的最短路上。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int N = 1010, M = 400010;
const long long inf = 1e18;
typedef pair<long long,int> pii;
typedef long long ll;
int n, m;
int h[N], e[M], ne[M], idx;
ll w[M], w1[M];
int h1[N], e1[M], ne1[M], idx1;
ll dist1[N], distn[N];
bool st[N];
int record[N];
int dfn[N], low[N], timestamp, root;
void add(int a,int b,ll c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
void add1(int a,int b,ll c)
{
e1[idx1] = b, w1[idx1] = c, ne1[idx1] = h1[a], h1[a] = idx1 ++;
}
void dijkstra(ll dist[], int u)
{
for(int i=1;i<=n;i++) dist[i] = inf;
memset(st,false,sizeof(st));
dist[u] = 0;
priority_queue<pii,vector<pii>,greater<pii> > heap;
heap.push({0,u});
while(heap.size()){
auto t = heap.top();
heap.pop();
int ver = t.second;
ll distance = t.first;
if(st[ver]) continue;
st[ver] = true;
for(int i=h[ver];~i;i=ne[i]){
int j = e[i];
if(dist[j]>distance+w[i]){
dist[j] = distance + w[i];
heap.push({dist[j], j});
}
}
}
}
void tarjan(int x)
{
dfn[x] = low[x] = ++ timestamp;
int flag = 0;
for(int i=h1[x];~i;i=ne1[i]){
int y = e1[i];
if(!dfn[y]){
flag ++;
tarjan(y);
low[x] = min(low[x], low[y]);
if(low[y]>=dfn[x]&&x!=root) record[x] = 1;
if(x==root&&flag>1) record[x] = 1;
}
else low[x] = min(low[x], dfn[y]);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
memset(h,-1,sizeof(h));
memset(h1,-1,sizeof(h1));
idx = idx1 = timestamp = 0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(record,0,sizeof(record));
while(m--){
int a,b;
ll c;
scanf("%d%d%lld",&a,&b,&c);
add(a,b,c), add(b,a,c);
}
dijkstra(dist1, 1);
dijkstra(distn, n);
for(int i=1;i<=n;i++){
if(dist1[i]+distn[i]==dist1[n]) record[i] = 2;
for(int j=h[i];~j;j=ne[j]){
int k = e[j];
ll ww = w[j];
if(dist1[i]+ww+distn[k]==dist1[n]){
add1(i,k,ww), add1(k,i,ww);
}
}
}
root = 1;
tarjan(1);
for(int i=1;i<=n;i++){
if(i==1||i==n) printf("1 ");
else printf("%d ",record[i]);
}
printf("\n");
}
return 0;
}