P6464 [传智杯 #2 决赛] 传送门

文章讲述了在一个教育机构中,如何通过在特定教学楼间安装一对传送门,使得任意两点之间的最短路长度之和最小。通过使用Floyd算法计算初始最短路径,然后针对每对可能的传送门位置进行优化,降低时间复杂度,解决最优化问题。
摘要由CSDN通过智能技术生成

题目描述 

P6464传送门

传智专修学院里有 n 栋教学楼,有 m 条双向通行道路连接这些教学楼,不存在重边和自环。每条道路都有一定的长度,而且所有教学楼之间都可以直接或者间接的通过道路到达。我们可以很容易的求出这些教学楼之间的最短路。

为了使交通更为顺畅,校方决定在两个教学楼里增设一对传送门。传送门可以将这对教学楼的距离直接缩短为 0。利用传送门,某些教学楼之间的最短路的距离就变短了。

由于预算有限,学校里只能安装一对传送门。但是校长希望尽可能方便学生,使任意两点之间的最短路长度的总和最小。当然啦,从 x 教学楼到 y 教学楼的长度和从 y 教学楼到 x 教学楼的长度只需要统计一次就可以了。

输入格式

输入第 1 行两个正整数n,m(n≤100,m≤n(n−1)/2),代表教学楼和道路数量。

接下来 m 行,每行三个正整数xi​,yi​,wi​(0<wi​≤104),表示在教学楼 xi​ 和 yi​ 之间,有一条长度为 wi​ 的道路。

输出格式

输出一行,在最优方案下的任意点对的最短道路之和。

输入输出样例

输入 #1复制

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

输出 #1复制

14

说明/提示

样例如图。当在 1 和 4 号教学楼架设一对传送门时,1 → 2 的最短路是 3,1 → 3 的最短路是 0+2,1 → 4 的最短路是 0,2 → 3 的最短路是 4,2 → 4 的最短路是 3+0,3 → 4 的最短路是 2,最短路之和是 14,是最佳方案。

分析:

这个题目所给的数据比较小又是最短路问题,可以联想到Floyd,由于在他想安装一个传送门来将所有点的距离和变得最小,那么可以想到的是在先对没有安装传送门时的最短路用Floyd找出来存放在数组 f 中然后一一枚举在每两个点之间安装传送门,在枚举的过程中如果我们用下面的代码那么就会面临超时警告。

ll floyd(){
    for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++)
            for(int k=1;k<=n;k++)
                g[i][k]=min(g[i][k],g[i][j]+g[j][k]);
    ll res=0;
    for(int i=1;i<=n;i++)
        for(int k=1;k<i;k++)
            res+=g[i][k];
    return res;
}

因为n的范围是100如果这样的话时间复杂度为O(n^5)这样一定会超时的,那么要如何才能不超时呢?我们可以这样想,每次我们在某两个点(i,k)之间安装传送门,那么安装传送门只会影响与(i,k)这两个点相连的点的距离所以我们可以分别将 i,k作为中间点然后更改g这样时间复杂度就变成了O(n^4)代码如下:

ll floyd(int a,int b){
    for(int i=1;i<=n;i++)
        for(int k=1;k<=n;k++)
            g[i][k]=min(g[i][k],g[i][a]+g[a][k]);
    for(int i=1;i<=n;i++)
        for(int k=1;k<=n;k++)
            g[i][k]=min(g[i][k],g[i][b]+g[b][k]);
    ll res=0;
    for(int i=1;i<=n;i++)
        for(int k=1;k<i;k++)
            res+=g[i][k];
    return res;
}

AC代码

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long

using namespace std;

const int N=110;
int f[N][N],g[N][N];
int n,m;
ll floyd(int a,int b){
    for(int i=1;i<=n;i++)
        for(int k=1;k<=n;k++)
            g[i][k]=min(g[i][k],g[i][a]+g[a][k]);
    for(int i=1;i<=n;i++)
        for(int k=1;k<=n;k++)
            g[i][k]=min(g[i][k],g[i][b]+g[b][k]);
    ll res=0;
    for(int i=1;i<=n;i++)
        for(int k=1;k<i;k++)
            res+=g[i][k];
    return res;
}
void init(){
    for(int i=1;i<=n;i++)
        for(int k=1;k<=n;k++)
            g[i][k]=f[i][k];
}
int main()
{
    IOS;
    cin>>n>>m;
    for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++)
        if(i==j)f[i][j]=0;
        else f[i][j]=1e9;
    for(int i=1;i<=m;i++){
        int x,y,c;cin>>x>>y>>c;
        f[y][x]=f[x][y]=min(f[x][y],c);
    }
    for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++)
            for(int k=1;k<=n;k++)
                f[i][k]=min(f[i][k],f[i][j]+f[j][k]);
    ll res=2e9;
    for(int i=1;i<=n;i++){
        for(int k=1;k<i;k++){
            init();
            g[k][i]=g[i][k]=0;
            ll x=floyd(i,k);
            res=min(res,x);
        }
    }
    cout<<res;
    return 0;
}

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值