预备役间学习记录22

一、刷题题解

1、# 无线通讯网

## 题目描述

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

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

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

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

## 输入格式

从 wireless.in 中输入数据第 1 行,2 个整数 $S$ 和 $P$,$S$ 表示可安装的卫星电话的哨所数,$P$ 表示边防哨所的数量。接下里 $P$ 行,每行两个整数 $x,y$ 描述一个哨所的平面坐标 $(x, y)$,以 km 为单位。

## 输出格式

输出 wireless.out 中

第 1 行,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$。

  1. 这题可以用kruskal算法;

  1. 但是,需要注意的是卫星电话,当我们已选边>=n-s时结束;

  1. 同时我们需要自己计算所有对节点的距离;

  1. 比如3个点就有3条边,现在有两个卫星电话,我们只需要1条最短的边,就可用kruskal算法选;

附代码

#include<bits/stdc++.h>
using namespace std;
long long x[1005],y[1005],fq[1005],zs;
long double zx=0;
int bs;
struct nod
{
    int X,Y;
    long double w;
}q[1000005];
bool bj(nod i,nod j)
{
    return i.w<j.w;
}
long double js(long long i,long long j) 
{   long long X=x[i]-x[j],Y=y[i]-y[j];
    return (long double)sqrt((long double)X*X+Y*Y);
}

int find(int i)
{
    if(fq[i]==i)return i;
    return fq[i]=find(fq[i]);
}
void join(int a,int b)
{
    if(find(a)==find(b))
        return;
    fq[find(a)]=b;
}

int main()
{
    long long s,n;
    cin>>s>>n;
    for(int i=1;i<=n;i++)
        cin>>x[i]>>y[i],fq[i]=i;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
                {q[++zs].X=i;q[zs].Y=j;q[zs].w=js(i,j);}
    sort(q+1,q+zs+1,bj);
    for(int i=1;i<=zs;i++)
        if(find(q[i].X)!=find(q[i].Y))
        {
            join(q[i].X,q[i].Y);
            bs++;
            if(bs>=n-s)
            {printf("%.2Lf",q[i].w);break;}  
        }
    return 0;
}

2、# 【模板】单源最短路径(弱化版)

## 题目背景

本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 [P4779](https://www.luogu.org/problemnew/show/P4779)

## 题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

## 输入格式

第一行包含三个整数 $n,m,s$,分别表示点的个数、有向边的个数、出发点的编号。

接下来 $m$ 行每行包含三个整数 $u,v,w$,表示一条 $u \to v$ 的,长度为 $w$ 的边。

## 输出格式

输出一行 $n$ 个整数,第 $i$ 个表示 $s$ 到第 $i$ 个点的最短路径,若不能到达则输出 $2^{31}-1$。

## 样例 #1

### 样例输入 #1

```

4 6 1

1 2 2

2 3 2

2 4 1

1 3 5

3 4 3

1 4 4

```

### 样例输出 #1

```

0 2 4 3

```

## 提示

【数据范围】

对于 $20\%$ 的数据:$1\le n \le 5$,$1\le m \le 15$;

对于 $40\%$ 的数据:$1\le n \le 100$,$1\le m \le 10^4$;

对于 $70\%$ 的数据:$1\le n \le 1000$,$1\le m \le 10^5$;

对于 $100\%$ 的数据:$1 \le n \le 10^4$,$1\le m \le 5\times 10^5$,$1\le u,v\le n$,$w\ge 0$,$\sum w< 2^{31}$,保证数据随机。

**Update 2022/07/29:两个点之间可能有多条边,敬请注意。**

对于真正 $100\%$ 的数据,请移步 [P4779](https://www.luogu.org/problemnew/show/P4779)。请注意,该题与本题数据范围略有不同。

样例说明:

![](https://cdn.luogu.com.cn/upload/pic/7641.png)

图片1到3和1到4的文字位置调换

  1. 这题是要求1点到其他点的最短路径,由于数据量过大我用floyd时间超限,我就用Dijkstra+stl优先队列+链式前向星;

  1. 首先我们将点的关系通过链式前向星,存储,接着,将开始节点,入队列,距离赋值0;

  1. 然后,进入循环(直到队列为空结束),取出队列第一个,在删除队列top,在判断取出的节点是否已选中,没有则进入下级循环;

  1. 然后进行,取出相邻的节点+取出的距离,和原本的距离比小就改变它,在将其入队列,循环直到没有相邻节点;

  1. 接着重复循环3、4;

附代码

#include<bits/stdc++.h>
#include<math.h>
using namespace std;

int n,m,s;
priority_queue<int,vector<pair<int,int> >,greater<pair<int,int> > >q;

struct node
{
 int last;
 int to;
 int qz;
}edge[500005];

int he;
int head[100005];
bool xz[100005];
int jl[100005];

void jr(int a,int b,int c)
{   he++;
    edge[he].last=head[a];
    edge[he].to=b;
    edge[he].qz=c;
    head[a]=he;
}

int main()
{
    for(int i=0;i<100005;i++)jl[i]=pow(2,31)-1;
    cin>>n>>m>>s;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        jr(a,b,c);
    }
    jl[s]=0;
    q.push(make_pair(0,s));
    while(!q.empty())
    {
        int dq=q.top().second; 
        q.pop();
        if(xz[dq]==true)continue;
        xz[dq]=true;
        for(int i=head[dq];i;i=edge[i].last)
        if(!xz[edge[i].to] && jl[edge[i].to]>jl[dq]+edge[i].qz)
        {
            jl[edge[i].to]=jl[dq]+edge[i].qz;
            q.push(make_pair(jl[edge[i].to],edge[i].to)); 
        }
    }
    for(int i=1;i<=n;i++)
    cout<<jl[i]<<' ';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值