其他分享
首页 > 其他分享> > 关键节点:dfs+最短路+割点

关键节点:dfs+最短路+割点

作者:互联网

 

题目描述

一个含有 n 个结点 m 条边的无向有权图,判断每个结点是否一定在从 1 到 n 的最短路径上 

输入格式

第一行一个整数 T,代表有 T 组测试数据
对于每一组测试数据,第一行有 2 个整数 n, m
接下来 m 行每行有 3 个整数 xi, yi, wi,表示 x 和 y 之间有一条权值为 wi 的边

输出格式

对于每组测试数据,在一行中输出 n 个整数,第 i 个整数代表 i 号结点的关键性
0 代表该结点不可能出现在从 1 到 n 的最短路径上
1 代表该结点出现在所有从 1 到 n 的最短路径上
2 代表该结点出现在部分从 1 到 n 的最短路径上 
1 ≤ T ≤ 1000
2 ≤ n ≤ 1000
n − 1 ≤ m ≤ min(n × (n − 1) / 2, 2⋅105)
1 ≤ wi ≤ 108 
∑n ≤ 2⋅105 
∑m ≤ 2⋅105
题目保证无重边无自环且连通

输入样例 复制

2
6 7
1 2 1
2 3 1
2 4 1
2 5 2
3 5 1
4 5 2
5 6 1
4 6
1 2 1
1 3 1
1 4 2
2 3 1
2 4 1
3 4 1

输出样例 复制

1 1 2 0 1 1
1 2 2 1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+10;
const ll inf=1e12;
int t,n,m,vis[N],ans[N],cnt,fir[N],low[N];
ll g[N][N],d1[N],dn[N];
struct nn{
    ll id,w;
    friend bool operator<(nn a,nn b){
        return a.w>b.w;
    }
};
priority_queue<nn>q;
void dij(int s,ll dis[]){//ll dis[]
    memset(vis,0,sizeof(vis));
    q.push({s,0});
    dis[s]=0;//不能写vis[s]=1;优先队列的vis[s]先不置1!!!因为while里面先判断为0才能往下走 
    while(!q.empty()){
        ll u=q.top().id,w=q.top().w;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=1;i<=n;i++){
            if(g[u][i]==inf||vis[i])continue;
            if(dis[u]+g[u][i]<dis[i]){
                dis[i]=dis[u]+g[u][i];  
                q.push({i,dis[i]});         
            }       
        }
    }
}
//fir存第一次遍历最短路(只访问最短路上的点),经过各个点的时态。low存最小的时态
int dfs(int s,int f){
    vis[s]=1,low[s]=++cnt,fir[s]=cnt;
    //更新low[s]
    for(int i=1;i<=n;i++){
        if(g[i][s]==inf) continue;
        if(vis[i]) low[s]=min(low[s],fir[i]);//比如:尾s碰头i,vis[i]=1
        else low[s]=min(low[s],dfs(i,s));//一路(从尾)更新回去类似并查集
    }
    //比较s和 f的fir时态大小(fir不会变)
    if(low[s]>=fir[f])ans[f]=1;//<说明不经过p可以直接到头,否则是割点
    return low[s];
}
int main(){
    int x,y;
    ll w;
    cin>>t;
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            d1[i]=dn[i]=inf; 
            for(int j=1;j<=n;j++){
                if(i==j) g[i][j]=0; 
                else g[i][j]=inf;
            }
        }
        while(m--){
            scanf("%d%d%lld",&x,&y,&w);
            g[x][y]=w;g[y][x]=w;
        }
        dij(1,d1);dij(n,dn);
        memset(ans,0,sizeof(ans));
		//若ij边是最短路上的边则点ij为最短路上的点。否则把不是最短路上的边删去即置inf
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++){//1~j~n. 1~i~n
                if(d1[i]+g[i][j]+dn[j]==d1[n]||d1[j]+g[j][i]+dn[i]==d1[n])
                    ans[i]=ans[j]=2;
                else g[i][j]=inf;
            }
        memset(vis,0,sizeof(vis));
        cnt=0;dfs(1,0);//dfs一下最短路径,找割点(即ans为1的点)
        ans[1]=ans[n]=1;
        for(int i=1;i<n;i++) cout<<ans[i]<<" ";
        cout<<ans[n]<<endl;
    }
}

标签:结点,int,短路,割点,dfs,vis,整数,ll,dis
来源: https://blog.csdn.net/weixin_50904510/article/details/121983663