关键结点(最短路+割点+建图)

题意

一个含有 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 1T1000
2 ≤ n ≤ 1000 2 \leq n \leq 1000 2n1000
n − 1 ≤ m ≤ 2 ∗ 1 0 5 n - 1 \leq m \leq 2*10^5 n1m2105
1 ≤ w i ≤ 1 0 8 1 \leq w_i \leq 10^8 1wi108

思路

首先判断这个点有没有可能出现在最短路上,判断方法是,用两个数组 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值