HDU2433 Travel 最短路径树

题目大意:
有一个n( n<=100 )个点m( m<=3000 )条边的无向图,边权为1,求删掉每一条边之后,每个点到所有点的最短距离和。
多组数据

题目分析:
最暴力的方法就是删掉每一条边,然后枚举每一个点,跑一边bfs,时间复杂度O(nm^2)
据说会TLE诶O(∩▽∩)O

于是看了大家的博客,知道了有最短路径树这种东西……
最短路径树就是对于一个源点,到每一个点都有一个最短路径,转移到这个点的点设为这个点的父亲,然后我们就有了一颗以源点为跟的树。
这颗树的每一条边都是源点到其他点的最短路径上的一条边。
也就是说删掉的边如果不是最短路径树上的边,那么对这个点到所有点的距离都没有影响。
那么我们先处理出每一个点的最短路径树。
删掉每一条边之后,枚举每一个点作为源点,如果删掉的这条边不是最短路径上的点,那就直接加上预处理出的答案,如果是,就重新跑一遍最短路。

只有当一条边在最短路径树上时会重新计算,每棵最短路径树有n条边,有n棵最小路径树,所以一共会重新计算n^2次。时间复杂度(n^2m)

代码如下:

#include 
#include 
#include 
#define N 120
#define M 12000
using namespace std;
int n,m,tmp;
int fa[N][N];
int fir[N],nes[M],v[M],tot=1;
int sum[N],dis[N];
bool pc[M],judge;
int dl[N],X[M],Y[M];
void edge(int x,int y)
{
    v[++tot]=y;
    nes[tot]=fir[x];
    fir[x]=tot;
    return;
}
#define edge(x,y) edge(x,y),edge(y,x)
int bfs(int S)
{
    memset(dis,-1,sizeof(dis));
    int l=1,r=1;
    dis[S]=0; dl[1]=S;
    fa[S][S]=0;
    static int c;
    int sum=0;
    while(l<=r)
    {
        c=dl[l++];
        sum+=dis[c];
        for(int t=fir[c];t;t=nes[t])
        {
            if(~dis[v[t]] || pc[t]) continue;
            dis[v[t]]=dis[c]+1;
            fa[S][v[t]]=c;
            dl[++r]=v[t];
        }
    }
    return r==n?sum:-1;
}
void init()
{
    tot=1;
    judge=true;
    memset(fir,0,sizeof(fir));
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&X[i],&Y[i]);
        edge(X[i],Y[i]);
    }
    for(int i=1;i<=n;i++)
        if((sum[i]=bfs(i))==-1)
        {
            judge=false;
            break;
        }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=1;i<=m;i++)
        {
            if(!judge) printf("INF\n");
            else
            {
                int ans=0;
                pc[i<<1]=true; pc[i<<1|1]=true;
                for(int j=1;j<=n;j++)
                {
                    if(fa[j][X[i]]!=Y[i] && fa[j][Y[i]]!=X[i]) ans+=sum[j];
                    else
                    {
                        if(~(tmp=bfs(j))) ans+=tmp;
                        else
                        {
                            ans=-1;
                            break;
                        }
                    }
                }
                if(~ans) printf("%d\n",ans);
                else printf("INF\n");
                pc[i<<1]=false; pc[i<<1|1]=false;
            }
        }
    }
    return 0;
}

你可能感兴趣的:(最短路径树)