• AcWing 3728. 城市通电(最小生成树)


    参考讲解:https://www.acwing.com/video/3213/

    题目

    平面上遍布着 n 座城市,编号 1∼n。
    第 i 座城市的位置坐标为 (xi,yi)。
    不同城市的位置有可能重合。
    现在要通过建立发电站和搭建电线的方式给每座城市都通电。
    一个城市如果建有发电站,或者通过电线直接或间接的与建有发电站的城市保持连通,则该城市通电。
    在城市 i 建立发电站的花费为 ci 元。
    在城市 i 与城市 j 之间搭建电线所需的花费为每单位长度 ki+kj 元。
    电线只能沿上下左右四个方向延伸,电线之间可以相互交叉,电线都是双向的。
    每根电线都是由某个城市沿最短路线搭建到另一个城市。
    也就是说,如果在城市 i 与城市 j 之间搭建电线,则电线的长度为 |xi−xj|+|yi−yj|。
    请问,如何合理设计通电方案,可以使得所有城市都成功通电,且花费最少?
    输出最少花费和具体方案。
    如果方案不唯一,则输出任意一种合理方案均可。

    输入输出

    输入:
    第一行包含整数 n。
    接下来 n 行,其中第 i 行包含两个整数 xi,yi,用来描述城市 i 的横纵坐标。
    再一行包含 n 个整数 c1,c2,…,cn,用来描述每个城市建立发电站的花费。
    最后一行包含 n 个整数 k1,k2,…,kn。
    输出:
    第一行输出所需要的最少花费。
    第二行输出一个整数 v,表示需要建立发电站的数量。
    第三行输出 v 个整数,表示建立发电站的城市编号,注意输出编号要在范围 [1,n] 内。且输出编号不应重复。输出编号顺序随意。
    第四行输出一个整数 e,表示需要搭建的电线数量。
    接下来 e 行,每行输出两个整数 a,b,表示要在城市 a 和 b 之间搭建电线。注意,任意两个城市之间最多只需要搭建一根电线,也就是说,对于每个 (a,b),不要有多余的 (a,b) 或 (b,a) 输出。a 和 b 不能相同,且要在范围 [1,n] 内。输出电线顺序随意。
    如果答案不唯一,输出任意合理方案即可。

    思路

    将城市建立发电站看作是城市连了一条边到超级发电站,即超级源点,边的权值为城市建立发电站的费用ci。城市也可以连电线通往有电的城市,即一条边,边的权值为城市之间的曼哈顿距离。
    当所有点与超级源点在一个连通块时,所有城市都有电。可以看成是最小生成树问题。
    答案需要求最小生成树的权值。
    该图是稠密图,用prim算法比Kruskal算法更好。

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #define x first
    #define y second
    
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    
    const int N = 2010;
    int n;
    PII q[N];   //n个城市的坐标
    int wc[N],wk[N];    //ci和ki
    LL dist[N]; //存放点距离连通块最短的距离
    int distp[N];    //存放当前距离连通块最短的点连到连通块的哪个点
    vector<int> ans1;   //记录建发电站的城市
    vector<PII> ans2;   //记录连电线的城市(一条边的两个顶点)
    bool st[N]; //记录当前点在prim中是否被访问过了
    
    LL get_dist(int a,int b){   //计算两个点之间的曼哈顿距离
        int dx = q[a].x - q[b].x;
        int dy = q[a].y - q[b].y;
        return (LL)(abs(dx) + abs(dy)) * (wk[a] + wk[b]);
    }
    LL prim(){
        LL res = 0;
        memset(dist, 0x3f, sizeof dist);
        for (int i = 1; i <= n; i ++ ){
            dist[i] = wc[i];    //初始化是都连发电站的值
        }
        dist[0] = 0;
        st[0] = true;
        for (int i = 0; i < n; i ++ ){  //将剩余n个点加入连通块中,操作n次
            int t = -1;
            for(int j = 1;j <= n;j++){  //选权值最小的点加入连通块
                if(!st[j] && (t == -1 || dist[t] > dist[j])) 
                    t = j;
            }
            st[t] = true;
            res += dist[t]; //将t点加入到答案中 
            if(!distp[t]) ans1.push_back(t);    //直接连到发电站的答案
            else ans2.push_back({distp[t],t});    //连电线的答案
            //从t点开始更新其他点到连通块的最小距离
            for (int j = 1; j <= n; j ++ ){
                if(!st[j] && get_dist(j,t) < dist[j]){  //更新到连通块的最短距离
                    dist[j] = get_dist(j,t);
                    distp[j] = t;
                }
            }
        }
        return res;
    }
    int main(){
        cin >> n;
        for (int i = 1; i <= n; i ++ )
            cin >> q[i].x >> q[i].y;
        for (int i = 1; i <= n; i ++ )
            cin >> wc[i];
        for (int i = 1; i <= n; i ++ )
            cin >> wk[i];
        LL res = prim();    //prim计算最小生成树的权值
        cout << res << endl;
        cout << ans1.size() << endl;    //输出发电站的数量
        for(int i = 0;i < ans1.size();i++)
            cout << ans1[i] << " ";
        cout << endl;
        cout << ans2.size() << endl;
        for (int i = 0; i < ans2.size(); i ++ )
            cout << ans2[i].x << " " << ans2[i].y << endl;
        return 0;
    }
    

    今天吃完晚饭出去逛了逛,分享今日美图:
    image

    作者:inss!w!
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 相关阅读:
    (十)HTTP.sys远程代码执行
    (九)不安全的HTTP方法
    (八)用户枚举
    (六)跨站请求伪造
    (五)跨站脚本攻击
    接口测试思路
    逆向工程,生成pojo、xml、mapper
    MyBatis框架核心之(五)注解使用resultMap及多表查询
    查看源代码Source not found及在eclipse中配置jdk的src.zip源代码
    EL有11个隐含对象
  • 原文地址:https://www.cnblogs.com/Hfolsvh/p/15046435.html
Copyright © 2020-2023  润新知