【算法题解】游乐场选址问题 - Floyd算法与最优选址策略

爱丽丝居住的城市由n个地区构成,第i个地区有pi​个居民。
这些地区由m条双向道路联接,使得对于任意两个地区可以通过这些道路相互到达。
第i条道路连接地区ui​和vi​,经过这条道路需要花费wi​的时间。
从一个地区经过若干条道路到达另一个地区所需的总时间为经过路径的时间的总和。
为了给居民们增加一些娱乐场所,爱丽丝打算选择一些地区建设游乐场。
但是由于经费问题,爱丽丝只能恰好选择2个不同的地区建设游乐场,居民们会自行选择前往距离更近的游乐场。
定义某个居民产生的代价是从居住地区到达最近游乐园所在地区所需的最少时间。
定义总代价为所有居民产生的代价的总和。
爱丽丝想要让总代价尽量小,你能帮她算出总代价最小是多少吗?

输入格式:

第一行输入2个整数n,m。表示地区数量和道路数量。
第二行输入n个整数,第i个数pi​表示第i个地区的居民数量。
接下来m行,每行输入三个整数ui​ ,vi​ ,wi​,表示地区ui​和地区vi​之间连接了一条需要花费wi​ 时间的道路。
2≤n≤300,1≤m≤105 ,1≤pi​≤105 ,1≤ui​ ,vi​≤n,ui​=vi​,1≤wi​≤105

输出格式:

输出仅一行,包含一个整数,表示最小总代价。

输入样例:

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

输出样例:

17

解题思路

关键点分析

  1. ​图论模型​​:将地区看作顶点,道路看作带权边,构建无向图
  2. ​最短路径​​:需要计算任意两点间的最短路径
  3. ​最优选址​​:枚举所有可能的两个选址组合,计算总代价并取最小值

算法选择

  • ​Floyd算法​​:适合稠密图(n≤300),可以高效计算所有顶点对的最短路径
  • ​暴力枚举​​:由于n较小,可以枚举所有可能的选址组合

代码实现与分析

 

cpp

复制

#include<bits/stdc++.h>
using namespace std;
#define int long long  // 使用长整型防止溢出
int C[5005][5005];    // 邻接矩阵存储最短路径
int D[5005];          // 存储每个地区的居民数量

signed main() {
    int n, m;
    cin >> n >> m;
    
    // 初始化邻接矩阵
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            C[i][j] = 100005;  // 初始化为较大值
        }
    }
    
    // 输入居民数量并初始化对角线
    for(int i = 1; i <= n; i++) {
        cin >> D[i];
        C[i][i] = 0;  // 自身到自身距离为0
    }
    
    // 输入道路信息
    for(int i = 1; i <= m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        C[a][b] = min(C[a][b], c);  // 处理重边,取最小值
        C[b][a] = C[a][b];          // 无向图对称
    }
    
    // Floyd算法计算所有点对最短路径
    for(int k = 1; k <= n; k++) {
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                C[i][j] = min(C[i][j], C[i][k] + C[k][j]);
            }
        }
    }
    
    // 枚举所有可能的选址组合
    int sum = 100005;  // 初始化为较大值
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            if(i == j) continue;  // 跳过相同地区
            int num = 0;
            // 计算总代价
            for(int k = 1; k <= n; k++) {
                num += D[k] * min(C[k][i], C[k][j]);
            }
            sum = min(sum, num);  // 更新最小值
        }
    }
    
    cout << sum << endl;
    return 0;
}

代码解析

  1. ​初始化阶段​​:

    • 邻接矩阵C初始化为较大值(100005)
    • 对角线元素设为0(地区到自身距离为0)
  2. ​输入处理​​:

    • 读取居民数量存入数组D
    • 读取道路信息,处理可能的重边(取最小权值)
  3. ​Floyd算法​​:

    • 三重循环动态更新所有点对的最短路径
    • 时间复杂度O(n³),对于n=300是可接受的
  4. ​最优选址计算​​:

    • 双重循环枚举所有可能的两个不同地区组合
    • 对于每个组合,计算所有居民到最近游乐场的总代价
    • 维护最小总代价sum
  5. ​输出结果​​:打印最小总代价

复杂度分析

  • ​时间复杂度​​:

    • Floyd算法:O(n³) = 300³ = 27,000,000次操作
    • 选址枚举:O(n²·n) = 300²×300 = 27,000,000次操作
    • 总复杂度:O(n³),对于n=300是可接受的
  • ​空间复杂度​​:

    • 邻接矩阵:O(n²) = 300×300 = 90,000个int
    • 居民数组:O(n) = 300个int

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值