入门OJ:最短路径树入门

题目描述

n个城市用m条双向公路连接,使得任意两个城市都能直接或间接地连通。其中城市编号为1..n,公路编号为1..m。任意个两个城市间的货物运输会选择最短路径,把这n*(n-1)条最短路径的和记为S。现在你来寻找关键公路r,公路r必须满足:当r堵塞之后,S的值会变大(如果r堵塞后使得城市u和v不可达,则S为无穷大)。

输入格式

第1行包含两个整数n,m,接下来的m行,每行用三个整数描述一条公路a,b,len(1<=a,b<=n),表示城市a和城市b之间的公路长度为len,这些公路依次编号为1..m。

n<=100,1<=m<=3000,1<=len<=10000。

输出格式

从小到大输出关键公路的编号。


先考虑暴力。

显然,对于一个点u,如果删去的边不在它到其它点的最短路上,那么S是不会变的。考虑到两点之间的最短路不止一条,我们可以给每个点先建出一棵最短路径树出来,然后枚举最短路径树上的每条边并把它删掉,再跑一遍最短路,看S是否变大即可。如果最短路用dijkstra+heap来做,这个过程的时间复杂度就是:

\[O(N*((N+M)log(N+M)+N*(N+M)log(N+M))) \]

也就N^3logN的级别。可以过这题。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#define maxn 101
#define maxm 3001
using namespace std;
 
vector<int> to[maxn],w[maxn],id[maxn];
int dis[maxn],treeid[maxn];
bool vis[maxn],lzs[maxm];
int n,m;
 
inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
 
inline void dijkstra(const int &s,const int &del){
    priority_queue< pair<int,int>,vector< pair<int,int> >,greater< pair<int,int> > > q;
    memset(vis,false,sizeof vis),memset(dis,0x3f,sizeof dis);
    q.push(make_pair(0,s)),dis[s]=0;
    while(q.size()){
        int u=q.top().second; q.pop();
        if(vis[u]) continue; vis[u]=true;
        for(register int i=0;i<to[u].size();i++) if(id[u][i]!=del){
            int v=to[u][i];
            if(dis[v]>dis[u]+w[u][i]){
                dis[v]=dis[u]+w[u][i],q.push(make_pair(dis[v],v));
                if(!del) treeid[v]=id[u][i];
            }
        }
    }
}
 
 
inline void out(int a){
    if(a>=10)out(a/10);
    putchar(a%10+'0');
}
 
int main(){
    n=read(),m=read();
    memset(dis,0x3f,sizeof dis);
    for(register int i=1;i<=m;i++){
        int u=read(),v=read(),_w=read();
        to[u].push_back(v),w[u].push_back(_w),id[u].push_back(i);
        to[v].push_back(u),w[v].push_back(_w),id[v].push_back(i);
    }
 
    for(register int i=1;i<=n;i++){
        dijkstra(i,0);
        int sum=0; for(register int j=1;j<=n;j++) sum+=dis[j];
        for(register int j=1;j<=n;j++) if(treeid[j]&&!lzs[treeid[j]]){
            dijkstra(i,treeid[j]);
            int cnt=0; for(register int k=1;k<=n;k++) cnt+=dis[k];
            if(cnt>sum) lzs[treeid[j]]=true;
        }
    }
 
    bool flag=false;
    for(register int i=1;i<=m;i++) if(lzs[i]){
        out(i),putchar('\n');
    }
    return 0;
}
posted @ 2019-05-30 20:09  修电缆的建筑工  阅读(302)  评论(0编辑  收藏  举报