LA 4080 Warfare A…

本文探讨了在存在重边的情况下如何利用第二短的边替代进行最短路径树构建的问题。通过SPFA算法求解所有节点的最短路径,并在此基础上优化路径选择,确保最小生成树的构建效率和准确性。

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

这是一个最短路树的题目 ,  这里有一个陷阱 , 有重边 , 如果有重边时必须用第二短的边来代替 。

解法:我们求出一个源点出发的最短路之后 , 我们得到的是一颗树 , 但是这颗树不一定是最小生成树 , 因此如果我们要删除的边在这颗树上 , 那么这个源点出发的最短路径长度就会改变 , 如果不在 , 那么就肯定不会改变,因此我们就只需要判断这条边是否存在这棵树上。

写代码时 , 一定要头脑清醒 , 不能出一点差错 , 不然 , 到后面来找时 , 很难找到
#include
#include
#include
#include
#include
#include
using namespace std;

const int MAXN = 110;
const long long INF = 100000000000000 ;
vectorgrap[MAXN];
long long g[MAXN][MAXN] , g1[MAXN][MAXN];
int n , m , p[MAXN][MAXN] , l = 0;
long long sum1 = 0 , d[MAXN][MAXN];

void init()
{
    for(int i = 1 ; i <= n; i++)
        grap[i].clear();
    memset(g , 0 , sizeof(g));
    memset(g1 , 0 , sizeof(g1));
    memset(p , 0 , sizeof(p));
    sum1 = 0;

}

long long spfa(int s)  // 初始化求所有最短路的和
{
    int i , done[MAXN] ;
    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] + g[u][v]))
            {
                d[s][v] = d[s][u] + g[u][v];
                p[s][v] = u;
                if(!done[v]) { q.push(v) ;  done[v] = 1; }
            }
        }
    }
    long long sum = 0;
    for(i = 1; i <= n; i++)
    {
        if(d[s][i] >= INF)  //  如果有某个点是孤立点
            sum += l;
        else sum += d[s][i] ;


    }
    return sum;
}

long long spfa1(int s)  // 初始化求所有最短路的和
{
    int i , done[MAXN] ;
    long long d1[MAXN] , sum = 0 ;
    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] + g[u][v]))
            {
                d1[v] = d1[u] + g[u][v];
                if(!done[v]) { q.push(v) ;  done[v] = 1; }
            }
        }
    }
    for(i = 1; i <= n; i++)
    {
        if(d1[i] >= INF)  //  如果有某个点是孤立点 , 那么就直接返回
            sum += l;
        else sum += d1[i] ;


    }
    return sum;
}

int main()
{
    while(scanf("%d %d %d" , &n , &m , &l) != EOF)
    {
        init();
        int i , j , k , b , x , y;
        long long  dist ;
        for(i = 0 ; i < m ; i++)
        {
            cin>>x>>y>>dist;
            if(g[x][y])
            {
                if(g[x][y] > dist)
                {
                    g1[x][y] = g1[y][x] = g[x][y] ;
                    g[x][y] = dist;
                    g[y][x] = dist;
                }
                else if(g1[x][y] == 0 || g1[x][y] > dist)  g1[x][y] = g1[y][x] = dist;
            }
            else
            {
                grap[x].push_back(y);
                grap[y].push_back(x);
                g[x][y] = dist;
                g[y][x] = dist;
            }
        }
        long long d_sum[MAXN] ;
        for(i = 1; i <= n; i++)
        {
            d_sum[i] = spfa(i);
            sum1 += d_sum[i];
        }

        long long max_sum  , sum2 = 0;
        for(i = 1; i <= n  ; i++)
            for(j = i+1 ; j <= n; j++)
            {
                max_sum = sum1 ;// 在这个地方 , wa了很多次 , max_sum的赋值必须放在if的外面。
                if(g[i][j])
                {
                    dist = g[i][j]; 
                    if(g1[i][j])
                        g[i][j] = g[j][i] = g1[i][j] ;
                    else  g[i][j] = g[j][i] = 0;

                    for(k = 1; k <= n; k++)
                    {
                        for(b = 1; b <= n; b++)
                            if((p[k][b] == i && b ==j) || (p[k][b] == j && b == i))  break;
                        if(b <= n)
                        {
                            max_sum -= d_sum[k];
                            max_sum += (long long)spfa1(k);
                        }
                    }
                    g[i][j] = g[j][i] = dist;

                }
                //cout<<max_sum<<endl;
                if(max_sum > sum2)
                {

                    sum2 = max_sum;
                }
            }
        cout<<sum1<<" "<<sum2<<endl;

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值