【洛谷P1991 P2872 P1265】几道生成树的抖机灵题目

早上并行操作的时候加训了几道

三道题 两黄一绿 想到了就不是难题
先看第一道:

P1991 无线通讯网

题目描述

国防部计划用无线网络连接若干个边防哨所。2 种不同的通讯技术用来搭建无线网络;

每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。

任意两个配备了一条卫星电话线路的哨所(两边都有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过 \(D\),这是受收发器的功率限制。收发器的功率越高,通话距离 \(D\) 会更远,但同时价格也会更贵。

收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个 \(D\)。你的任务是确定收发器必须的最小通话距离 \(D\),使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。

输入格式

第一行,\(2\) 个整数 \(S\)\(P\)\(S\) 表示可安装的卫星电话的哨所数,\(P\) 表示边防哨所的数量。

接下里 \(P\) 行,每行两个整数 \(x,y\) 描述一个哨所的平面坐标 \((x, y)\),以 km 为单位。

输出格式

第一行,\(1\) 个实数 \(D\),表示无线电收发器的最小传输距离,精确到小数点后两位。

输入输出样例 #1

输入 #1

2 4
0 100
0 300
0 600
150 750

输出 #1

212.13

说明/提示

数据范围及约定

  • 对于 \(20\%\) 的数据:\(P = 2,S = 1\)
  • 对于另外 \(20\%\) 的数据:\(P = 4,S = 2\)
  • 对于 \(100\%\) 的数据保证:\(1 ≤ S ≤ 100\)\(S < P ≤ 500\)\(0 ≤ x,y ≤ 10000\)

解法&&个人感想

题目可能有点难理解 但是大概意思说 所有卫星电话无视距离可以直接通话
所以 最终状态是题目中有S个联通块
而后面的“每一对哨所之间至少有一条通话路径” 明显是最小生成树了吧?
这里肯定用Kruscal的
下面看代码:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define maxm 500001
#define maxn 505
using namespace std;
struct Edge{
    int from,to;
    float w;
};
Edge edge[maxm];
int s,p;
int x[maxn],y[maxn];
int fa[maxn];
int cnt,tot;
double ans=0;
bool cmp(Edge x,Edge y){
    return x.w<y.w;
}
int get(int x){
    if(fa[x]==x) return x;
    return fa[x]=get(fa[x]);
}
void merge(int x,int y){
    fa[x]=y;
}
int main(){
    scanf("%d%d",&s,&p);
    for(int i=1;i<=p;i++){
        scanf("%d%d",&x[i],&y[i]);
    }
    for(int i=1;i<=p;i++) fa[i]=i;
    for(int i=1;i<=p;i++){
        for(int j=i+1;j<=p;j++){
            edge[++cnt].from=i;
            edge[cnt].to=j;
            edge[cnt].w=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
        }
    }
    sort(edge+1,edge+1+cnt,cmp);
    for(int i=1;i<=cnt;i++){
        int x=get(edge[i].from),y=get(edge[i].to);
        if(x==y) continue;
        merge(x,y);
        ans=edge[i].w;
        tot++;
        if(tot==p-s){
            break;
        }
    }
    printf("%.2lf",ans);
    system("pause");
    return 0;
}

下面看第二题:

P2872 [USACO07DEC] Building Roads S

题目描述

Farmer John had just acquired several new farms! He wants to connect the farms with roads so that he can travel from any farm to any other farm via a sequence of roads; roads already connect some of the farms.

Each of the N (1 ≤ N ≤ 1,000) farms (conveniently numbered 1..N) is represented by a position (Xi, Yi) on the plane (0 ≤ Xi ≤ 1,000,000; 0 ≤ Yi ≤ 1,000,000). Given the preexisting M roads (1 ≤ M ≤ 1,000) as pairs of connected farms, help Farmer John determine the smallest length of additional roads he must build to connect all his farms.

给定 \(n\) 个点的坐标,第 \(i\) 个点的坐标为 \((x_i,y_i)\),这 \(n\) 个点编号为 \(1\)\(n\)。给定 \(m\) 条边,第 \(i\) 条边连接第 \(u_i\) 个点和第 \(v_i\) 个点。现在要求你添加一些边,并且能使得任意一点都可以连通其他所有点。求添加的边的总长度的最小值。

输入格式

* Line 1: Two space-separated integers: N and M

* Lines 2..N+1: Two space-separated integers: Xi and Yi

* Lines N+2..N+M+2: Two space-separated integers: i and j, indicating that there is already a road connecting the farm i and farm j.

第一行两个整数 \(n,m\) 代表点数与边数。
接下来 \(n\) 行每行两个整数 \(x_i,y_i\) 代表第 \(i\) 个点的坐标。
接下来 \(m\) 行每行两个整数 \(u_i,v_i\) 代表第 \(i\) 条边连接第 \(u_i\) 个点和第 \(v_i\) 个点。

输出格式

* Line 1: Smallest length of additional roads required to connect all farms, printed without rounding to two decimal places. Be sure to calculate distances as 64-bit floating point numbers.

一行一个实数代表添加的边的最小长度,要求保留两位小数,为了避免误差, 请用 \(64\) 位实型变量进行计算。

输入输出样例 #1

输入 #1

4 1
1 1
3 1
2 3
4 3
1 4

输出 #1

4.00

说明/提示

数据规模与约定

对于 \(100\%\) 的整数,\(1 \le n,m \le 1000\)\(1 \le x_i,y_i \le 10^6\)\(1 \le u_i,v_i \le n\)

说明

Translated by 一只书虫仔。

解法&&个人感想

这道题的关键在于“已经修好的路”要怎么处理
我一开始的想法是在Edge结构体里加一个判断变量
但是,这样边权不会改变 如果存在很多更小的边的话,就会导致那些边被优先选择
这样是不行的
所以 我们要将已经赋值的边 赋值为0
保证其被优先选择
下面看代码:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define maxn 5005
#define maxm 500005
using namespace std;
int n,m;
int x[maxn],y[maxn];
struct Edge{
    int from,to,pd;
    double w;
};
Edge edge[maxm];
int fa[maxn];
int cnt=0;
int u,v;
double ans=0;
int k=0;
bool cmp(Edge x,Edge y){
    return x.w<y.w;
} 
int get(int x){
    if(fa[x]==x) return x;
    return fa[x]=get(fa[x]);
}
void merge(int x,int y){
    fa[x]=y;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x[i],&y[i]);
    }
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            edge[++cnt].from=i;
            edge[cnt].to=j;
            edge[cnt].w=sqrt((ll)(x[i]-x[j])*(ll)(x[i]-x[j])+(ll)(y[i]-y[j])*(ll)(y[i]-y[j]));
        }
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        int mi=min(u,v),ma=max(u,v);
        for(int j=1;j<=cnt;j++){
            if(mi==edge[j].from&&ma==edge[j].to){
                edge[j].w=0;
                break;
            }
        }
    }
    sort(edge+1,edge+1+cnt,cmp);
    for(int i=1;i<=cnt;i++){
        int x=get(edge[i].from),y=get(edge[i].to);
        if(x==y) continue;
        merge(x,y);
        ans+=edge[i].w;
    }
    printf("%.2lf",ans);
    system("pause");
    return 0;
}

下面看第三道题:

P1265 公路修建

题目描述

某国有 \(n\) 个城市,它们互相之间没有公路相通,因此交通十分不便。为解决这一“行路难”的问题,政府决定修建公路。修建公路的任务由各城市共同完成。

修建工程分若干轮完成。在每一轮中,每个城市选择一个与它最近的城市,申请修建通往该城市的公路。政府负责审批这些申请以决定是否同意修建。

政府审批的规则如下:

  1. 如果两个或以上城市申请修建同一条公路,则让它们共同修建;
  2. 如果三个或以上的城市申请修建的公路成环。如下图,A 申请修建公路 AB,B 申请修建公路 BC,C 申请修建公路 CA。则政府将否决其中最短的一条公路的修建申请;
  3. 其他情况的申请一律同意。

一轮修建结束后,可能会有若干城市可以通过公路直接或间接相连。这些可以互相连通的城市即组成“城市联盟”。在下一轮修建中,每个“城市联盟”将被看作一个城市,发挥一个城市的作用。

当所有城市被组合成一个“城市联盟”时,修建工程也就完成了。

你的任务是根据城市的分布和前面讲到的规则,计算出将要修建的公路总长度。

输入格式

第一行一个整数 \(n\),表示城市的数量。(\(n \leq 5000\)

以下 \(n\) 行,每行两个整数 \(x\)\(y\),表示一个城市的坐标。(\(-10^6 \leq x,y \leq 10^6\)

输出格式

一个实数,四舍五入保留两位小数,表示公路总长。(保证有唯一解)

输入输出样例 #1

输入 #1

4
0 0
1 2
-1 2
0 4

输出 #1

6.47

说明/提示

修建的公路如图所示:

解法&&个人感想

我一开始就觉得这个条件二怪怪的 你删掉一条边 并不影响它们的连通性
然后就按照正常思路做了
后面看题解才发现 我测 这个条件压根不成立 搞屁啊
虽然最后结果是一样的就是了
但是这题有几个需要注意的点:
1.$ 10^6 \(的范围平方会爆int 需要强制转ll 2.这题m比较大 所以不能用heap-prim或者前向星的prim 要用最暴力的O(\) n^2 $)的prim
这很坑
下面看代码:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define maxn 5005
#define maxm 50000005
using namespace std;
int n,tot;
int x[maxn],y[maxn];
double d[maxn];
int vis[maxn];
double ans=0;
double query(int i,int j){
    return sqrt((ll)(x[i]-x[j])*(ll)(x[i]-x[j])+(ll)(y[i]-y[j])*(ll)(y[i]-y[j]));
}
void prim(){
    for(int i=0;i<=n;i++) d[i]=1e8*1.0;
    d[1]=0.0;
    for(int i=1;i<=n;i++){
        int x=0;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&d[j]<d[x]) x=j;
        }
        vis[x]=1;
        for(int y=1;y<=n;y++){
            if(!vis[y]&&d[y]>query(x,y)){
                d[y]=query(x,y);
            }
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x[i],&y[i]);
    }
    prim();
    for(int i=1;i<=n;i++) ans+=d[i];
    printf("%.2lf",ans);
    system("pause");
    return 0;
}

放图
image

posted @ 2025-04-07 12:45  elainafan  阅读(47)  评论(0)    收藏  举报