首页 > 其他分享 >【蓝桥杯集训·每日一题】AcWing 3728. 城市通电

【蓝桥杯集训·每日一题】AcWing 3728. 城市通电

时间:2023-05-21 10:06:05浏览次数:50  
标签:输出 电线 int 城市 花费 蓝桥 发电站 3728 AcWing

写在前面

本人CSDN博客主页:这里

一、题目

1、原题链接

3728. 城市通电

2、题目描述

平面上遍布着 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] 内。输出电线顺序随意。

如果答案不唯一,输出任意合理方案即可。

数据范围

对于前三个测试点,1≤n≤3对于全部测试点,1≤n≤2000,1≤xi,yi≤106,1≤ci,ki≤109

输入样例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

二、解题报告

1、思路分析

思路来源:y总讲解视频 y总yyds

(1)建立一个虚拟源点,由虚拟源点向每个建有发电站的城市连一条边,边权为建立该发电站的花费。 (2)这样就转化为了一个最小生成树问题,求包括虚拟源点在内的图的最小生成树。 (3)利用prim算法进行求解即可。

2、时间复杂度

时间复杂度为O(n2+m)

3、代码详解

#include <iostream>
#include <utility>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N=2010;
int n;
int c[N],k[N],fa[N]; //fa[]存储每个点的父结点,即建有发电站的城市
PII p[N];     //存储每个点的坐标,first为横坐标,second为纵坐标
LL dist[N];   //存储每个点到当前连通块的距离
bool st[N];   //存储每个点是否已经在连通块中
vector<int> ans1;     //存储所有需要建发电站的城市
vector<PII> ans2;     //存储城市之间需要搭建电线的两个城市
//两点之间建设电线的花费
LL distance(int a,int b){    //注意类型,可能超过int范围
    int x=abs(p[a].first-p[b].first);
    int y=abs(p[a].second-p[b].second);
    return (LL)(x+y)*(k[a]+k[b]);
}
//prim算法求最小生成树
LL prim(){     //注意返回类型,可能超过int范围
    memset(dist,0x3f,sizeof dist);
    dist[0]=0;
    st[0]=true;
    LL res=0;
    //初始将0号点,虚拟源点已经加入连通块中,所以每个点到虚拟源点的距离(花费)为在该点建设发电站的花费
    for(int i=1;i<=n;i++) dist[i]=c[i];
    for(int i=0;i<n;i++){
        int t=-1;
        //每次寻找距离连通块的距离最小的点
        for(int j=1;j<=n;j++){
            if(!st[j]&&(t==-1||dist[j]<dist[t])) t=j;
        }
        res+=dist[t];
        st[t]=true;
        if(!fa[t]) ans1.push_back(t);     //如果t没有父结点,则说明需要在该点建立发电站,将其加入答案中
        else ans2.push_back({fa[t],t});   //否则,两点之间需要搭建电线
        //用t来更新其他点到连通块的距离
        for(int j=1;j<=n;j++){
            if(dist[j]>distance(j,t)){
               dist[j]=distance(j,t);
               fa[j]=t;
            } 
        }
    }
    return res;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>p[i].first>>p[i].second;
    for(int i=1;i<=n;i++) cin>>c[i];
    for(int i=1;i<=n;i++) cin>>k[i];
    cout<<prim()<<endl;
    cout<<ans1.size()<<endl;
    for(int i=0;i<ans1.size();i++) cout<<ans1[i]<<' ';
    cout<<endl<<ans2.size()<<endl;
    for(int i=0;i<ans2.size();i++) cout<<ans2[i].first<<' '<<ans2[i].second<<endl;
    return 0;
}

三、知识风暴

Prim算法

  • 基本思想:先将起始点加入连通块中,然后寻找距离连通块最近的顶点然后再加入,同时利用该点更新其他点距离连通块的距离,然后再将距离连通块最近的点加入,循环往复,直到将所有点加入,得出最小生成树,如果没有将所有点都加入说明不存在最小生成树。

标签:输出,电线,int,城市,花费,蓝桥,发电站,3728,AcWing
From: https://blog.51cto.com/u_15720469/6318608

相关文章

  • [每天例题]蓝桥杯 C语言 次数差
    次数差题目  思路分析1.通过字符型数组接收字符串,通过整形数组确定字母出现的次数2.通过for—if寻找出现次数最多与最少的字母,注意,这里有个坑,出现次数最少的字母至少出现一次代码#include<stdio.h>intmain(){ chars[1000]; intnum[26]={0}; intmax=-1,min=10......
  • [每天例题]蓝桥杯 C语言 回文日期
    回文日期题目    思路分析1.由于题目要求是找到一定范围日期内的回文日期,所以我们可以采用for遍历日期2.再调用函数先判断闰年,再进行日期合法判断,最后再进行回文数判断3.注意,该日期范围包含起始和结束这两个日期,这里会有一个案例挖坑代码#include<stdio.h>int......
  • [每天例题]蓝桥杯 C语言 笨小猴
    笨小猴题目  思路分析1.首先难点是找出出现次数最多与最少的字母,我们可以通过建立两个数组,一个是字符数组,用来存储字符串,一个是整形数组,用来记录每个字母对应的出现次数,然后再使用for—if配合找出最大最小数2,第二个可以通过调用函数来确定差值是否为素数代码#include<......
  • [每天例题]蓝桥杯 C语言 字符统计
    字符统计题目思路分析1.建立字符数组,存储字符串2.建立整形数组,储存对应字母出现的次数3.使用for循环进行排序,使用if判断最大最小值代码#include<stdio.h>intmain(){chara[1000000];intnum[26]={0};inti;intmax=0;scanf("%s",&a);......
  • 2021蓝桥杯国B
     《A填空问题》试题A:带宽我觉得题目出错了,在计算机网络中带宽中的bps是bit/s其中的单位M是10^6而不再是按照2^20来算了但是答案不是这样的,奇怪! 试题B:纯质数 死亡原因:没有把0设置为非质数其余的主要是用线性筛筛出1~20210605中的质数就好啦in......
  • AcWing795.前缀和
    输入一个长度为n的整数序列。接下来再输入m个询问,每个询问输入一对l,r。对于每个询问,输出原序列中第l个数到第r个数的和。数据范围:1≤l≤r≤n,1≤n,m≤100000,-1000≤数列中元素的值≤1000。 #include<iostream>//C++标准库中的头文件.用于控制台输入和输出。#includ......
  • P8597 [蓝桥杯 2013 省 B] 翻硬币
    #include<bits/stdc++.h>usingnamespacestd;chara[1010],b[1010];intans;intkey=0;//置为0表示关闭计数intmain(){scanf("%s",a);scanf("%s",b);for(inti=0;a[i]!='\0';i++){if(a[i]!=b[i]&&......
  • 蓝桥杯 2023 省 A 网络稳定性
    蓝桥杯撞题NOIP原题,做法也一模一样(撞题:NOIP2013提高组货车运输)由题意可得这是让我们先求一个最大生成树(把求最小生成树反过来求即可),再求最小边权。求最大生成树我们可以用并查集+排序做出。求最小边权我们可以LCA,也可以树链剖分+线段树维护。后者码量太大(本人太懒),没打算写。......
  • AcWing 809. 最小公倍数
    AcWing809.最小公倍数1.地址https://www.acwing.com/problem/content/811/2.题解#include<iostream>#include<cstdio>#include<algorithm>usingnamespacestd;/*两数乘积=两数的最大公因数×两数的最小公倍数*///代表求最大公因数的函数intgcd(i......
  • 【acwing】Django课程笔记
     工程课Linux-8.0.SSH的简易安全配置-AcWing  (避免服务器被偷家)怎样修改远程登录的端口?_弹性云服务器ECS_常见问题_登录与连接_远程连接类_华为云(huaweicloud.com)vim/etc/ssh/sshd_configservicesshdrestart ......