城市通电
城市通电
平面上遍布着 座城市,编号 。
第 座城市的位置坐标为 。
不同城市的位置有可能重合。
现在要通过建立发电站和搭建电线的方式给每座城市都通电。
一个城市如果建有发电站,或者通过电线直接或间接的与建有发电站的城市保持连通,则该城市通电。
在城市 建立发电站的花费为 元。
在城市 与城市 之间搭建电线所需的花费为每单位长度 元。
电线只能沿上下左右四个方向延伸,电线之间可以相互交叉,电线都是双向的。
每根电线都是由某个城市沿最短路线搭建到另一个城市。
也就是说,如果在城市 与城市 之间搭建电线,则电线的长度为 。
请问,如何合理设计通电方案,可以使得所有城市都成功通电,且花费最少?
输出最少花费和具体方案。
如果方案不唯一,则输出任意一种合理方案均可。
输入格式
第一行包含整数 。
接下来 行,其中第 行包含两个整数 ,用来描述城市 的横纵坐标。
再一行包含 个整数 ,用来描述每个城市建立发电站的花费。
最后一行包含 个整数 。
输出格式
第一行输出所需要的最少花费。
第二行输出一个整数 ,表示需要建立发电站的数量。
第三行输出 个整数,表示建立发电站的城市编号,注意输出编号要在范围 内。且输出编号不应重复。输出编号顺序随意。
第四行输出一个整数 ,表示需要搭建的电线数量。
接下来 行,每行输出两个整数 ,表示要在城市 和 之间搭建电线。注意,任意两个城市之间最多只需要搭建一根电线,也就是说,对于每个 ,不要有多余的 或 输出。 和 不能相同,且要在范围 内。输出电线顺序随意。
如果答案不唯一,输出任意合理方案即可。
数据范围
对于前三个测试点,。
对于全部测试点,,,。
输入样例1:
3 2 3 1 1 3 2 3 2 3 3 2 3
输出样例1:
8 3 1 2 3 0
输入样例2:
3 2 1 1 2 3 3 23 2 23 3 2 3
输出样例2:
27 1 2 2 1 2 2 3
解题思路
看上去像是个求最小生成树的问题,但实际上要保证每个城市通电不一定是一棵树的形式,只要每个连通块内存在一个点是发电站就可以。有个trick就是建立一个超级源点,如果某个点是发电站,那么这个点就与超级源点连一条边,边的权重就是在这个点建立发电站的代价,这样就把若干个连通块变成一个连通块,变成了求最小生成树的问题。
对于原图中的每一个合法方案,由于每一个连通块中的点都与发电站连通,而在新图中每个发电站都与超级源点相连,因此每个点都与超级源点连通,因此就对应一颗生成树(原图中不存在环,否则去掉某条边代价会变小,因此得到的连通图是一颗生成树)。因此原图中的每一个方案都对应新图中的一棵生成树。对于原图中每个方案的代价,在新图中与之对应的代价也是一样的。反过来在新图中的每个方案,如果某个点与超级源点相连,那么在这个点建发电站,否则其余的边不变,代价与对应的原图也是一样的。因此两个集合中的元素是一一对应的,求原图中方案的最小值就等价于求新图中方案的最小值。而新图中的最小值就是所有生成树中的最小生成树。
这里求最小生成树用 Kruskal,时间复杂度为,其中。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 2010, M = N * N; 7 8 struct Node { 9 int v, w; 10 LL wt; 11 12 bool operator<(Node &t) { 13 return wt < t.wt; 14 } 15 }e[M]; 16 int x[N], y[N]; 17 int a[N], b[N]; 18 int fa[N]; 19 20 int find(int x) { 21 return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); 22 } 23 24 int main() { 25 int n; 26 scanf("%d", &n); 27 for (int i = 1; i <= n; i++) { 28 scanf("%d %d", x + i, y + i); 29 } 30 int sz = 0; 31 for (int i = 1; i <= n; i++) { 32 scanf("%d", a + i); 33 e[sz++] = {0, i, a[i]}; // 设第0个点为超级源点,发电站与0号点连一条边 34 } 35 for (int i = 1; i <= n; i++) { 36 scanf("%d", b + i); 37 } 38 for (int i = 1; i <= n; i++) { // 完全图,每两个点建一条边 39 for (int j = 1; j < i; j++) { 40 e[sz++] = {i, j, (LL)(abs(x[i] - x[j]) + abs(y[i] - y[j])) * (b[i] + b[j])}; 41 } 42 } 43 sort(e, e + sz); 44 for (int i = 0; i <= n; i++) { 45 fa[i] = i; 46 } 47 LL ret = 0; 48 vector<int> ans1; 49 vector<vector<int>> ans2; 50 for (int i = 0; i < sz; i++) { // 求最小生成树 51 int v = e[i].v, w = e[i].w; 52 LL wt = e[i].wt; 53 int pv = find(v), pw = find(w); 54 if (pv != pw) { 55 fa[pv] = pw; 56 ret += wt; 57 if (!v) ans1.push_back(w); 58 else ans2.push_back({v, w}); 59 } 60 } 61 printf("%lld\n%d\n", ret, ans1.size()); 62 for (auto &x : ans1) { 63 printf("%d ", x); 64 } 65 printf("\n%d\n", ans2.size()); 66 for (auto &p : ans2) { 67 printf("%d %d\n", p[0], p[1]); 68 } 69 70 return 0; 71 }
参考资料
AcWing 3728. 城市通电(蓝桥杯集训·每日一题):https://www.acwing.com/video/4650/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17198306.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效