hdu 2433 最短路树

本文探讨了在无向图中删除一条边后,如何使用SPFA算法快速更新所有点间的最短路径,并通过实例展示了具体操作过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个题目的意思是: 题目给出一个无向图 , 问你删除其中一条边之后 , 所有点与点之间的最短路和 。

一开始做这个题目的时候 , 用的是floyd算法的暴力解法 , 结果肯定是超时的。
后面学到了最短路树之后才过掉 。
当每个源点去求最短路时 , 就用一个数组来记录所产生的最短路树 ,
然后后面在再这个树中查找, 是否存在删除的边 , 如果不存在 , 那么这个源点就不要求过最短路时间复杂度为O(1)。

这题目对于求最短路也可以用bfs来做, 因为每条边的权值都是一样的。

代码:  time : 1089ms
#include
#include
#include
#include
#include
using namespace std;

const int MAXN = 105;
const int INF = 10000000 ;
vectorgrap[MAXN];  //  邻接法记录图
int g[MAXN][MAXN] ;
int n , m , bian[3100][2] , d[MAXN][MAXN] , p[MAXN][MAXN] , sum1 = 0 , bz = 0;

void init()
{
    for(int i = 1 ; i <= n; i++)
        grap[i].clear();
    memset(g , 0 , sizeof(g));  //  记录每条边的条数(因为所有边的权都是1 , 所以没必要记录变的权)
    memset(p , 0 , sizeof(p));  //  记录每个点为源点求最短路时所形成的最短路树。
    sum1 = 0;
    bz = 0;
}

int spfa(int s)  // 初始化求所有最短路的和
{
    int i , done[MAXN] , sum = 0;
    memset(done , 0 , sizeof(done));
    for(i = 1; i <= n; i++)  d[s][i] = INF ;
    d[s][s] = 0;
    queueq ;
    q.push(s);  //  队列记录下点
    while(!q.empty())
    {
        int u = q.front() ; q.pop() ;
        done[u] = 0;
        for(i = 0 ; i < grap[u].size() ; i++)
        {
            int v = grap[u][i];
            if(d[s][v] > (d[s][u] + 1))
            {
                d[s][v] = d[s][u] + 1;
                p[s][v] = u;
                if(!done[v]) { q.push(v) ;  done[v] = 1; }
            }
        }
    }
    for(i = 1; i <= n; i++)
    {
        sum += d[s][i] ;
        if(d[s][i] == INF)  //  如果有某个点是孤立点 , 那么就直接返回
        {
            bz = 1;  // 记录存在点是孤立点
            return sum ;
        }
    }
    return sum;
}

int spfa1(int s)  // 当边改变时求的最短路和 , 用法和上面那个spfa一样
{
    int i , done[MAXN] , sum = 0 , d1[MAXN] ;
    memset(done , 0 , sizeof(done));
    for(i = 1; i <= n; i++)  d1[i] = INF ;
    d1[s] = 0;
    queueq ;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front() ; q.pop() ;
        done[u] = 0;
        for(i = 0 ; i < grap[u].size() ; i++)
        {
            int v = grap[u][i];
            if(g[u][v] && d1[v] > ( d1[u] + 1 ))
            {
                d1[v] = d1[u] + 1;
                if(!done[v])
                { q.push(v) ; done[v] = 1;}
            }
        }
    }
    for(i = 1; i <= n; i++)
    {
        sum += d1[i] ;
        if(d1[i] >= INF)
        {
            bz = 1;
            return sum;
        }
    }
    return sum;
}

int main()
{
    while(scanf("%d %d" , &n , &m) != EOF)
    {
        init();
        int i , j , x , y , k;
        for(i = 0 ; i < m ; i++)
        {
            scanf("%d %d" , &x , &y);
            if(g[x][y])  // 当有重边时 , 就没有必要在邻接表中记录
            {
                g[x][y] += 1;
                g[y][x] += 1;
                bian[i][0] = x;
                bian[i][1] = y;
            }
            else
            {
                grap[x].push_back(y);
                grap[y].push_back(x);
                g[x][y] += 1;
                g[y][x] += 1;
                bian[i][0] = x;
                bian[i][1] = y;
            }
        }
        int d_sum[MAXN] ;
        for(i = 1; i <= n; i++)
        {
            d_sum[i] = spfa(i); // 记录初始化时每个源点所产生的最短路和
            sum1 += d_sum[i];  // 记录初始时, 所有最短路的和
            if(bz)  break; 
        }
        //cout<<sum1<<endl;
        if(bz) // 如果存在孤立点 , 那么所有边改变都是输出INF
            for(i = 0 ; i < m ; i++)  printf("INF\n");
        else
        {
            int max_sum ;
            for(i = 0; i < m ; i++)
            {
                x = bian[i][0] ;  y = bian[i][1];
                if(g[x][y] > 1)  printf("%d\n" , sum1);  //  是否存在重边
                else
                {
                    max_sum = sum1 ;
                    bz = 0;
                    g[x][y] = g[y][x] = 0;
                    for(j = 1; j <= n; j++)
                    {
                        for(k = 1; k <= n; k++)
                            if((p[j][k] == x && k == y) || (p[j][k] == y && k == x))  break; // 改变的边是否在该最短路树中
                        if(k <= n)
                        {
                            max_sum -= d_sum[j];
                            max_sum += spfa1(j);  //  改变所有最短路的和
                            if(bz)  break;
                        }
                    }
                    if(bz)  printf("INF\n");  //  如果改变边之后存在孤立点 ,  就直接输出INF
                    else printf("%d\n" , max_sum);
                    g[x][y] = g[y][x] = 1; //  恢复改变的边
                }
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值