学习日记(最小生成树的例题练习)

# 无线通讯网

## 题目描述

国防部计划用无线网络连接若干个边防哨所。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$。

思路

这个题本质上就是考的最小生成树的俩个算法,主要就是难以找到突破点,由题目可以知道,只要俩哨点之间配备了卫星电话,就可以无视距离地进行通话,那么就相当于删除了一条边,如果至于一个卫星电话和没有卫星电话是一样的。那么如果具有了俩个以上的卫星电话,此时的情况就可以大致分为俩种,一种是俩个哨点之间本来就有联系,另一种是没有直接的联系,如果是第一种情况,那么此时最小距离应该为此时的第二大距离;如果是第二种情况,那么就要重新考虑新的树;

 上图就是俩个卫星点的时候,那么此时必然要重新构建一个树。

如果此时再加入一个卫星点:

 

 此时其实可以发现,如果把有卫星电话的哨点放在一堆,反正每个点之间也没有影响,那么就可以知道最小的距离应该选什么了;

代码 

#include<stdio.h>
#include<math.h>
#define n 250001//注意大小

int s,p;
int a,b,x[510],y[510];//表示点
int f[501],num;
int k=0;

typedef struct//边,点,边长的结构体
{
    int a,b;
    double v;
} Edge;
//快速排列
void swap(Edge E[],int low,int high)
{
    Edge temp;
    temp = E[low];
    E[low] = E[high];
    E[high] = temp;

}

int fun(Edge E[],int low,int high)
{
    double key;
    Edge lowx;
    lowx=E[low];
    key=E[low].v;
    while(low<high)
    {
        while(low<high && E[high].v>=key)
            high--;
        if(low<high)
            swap(E,low,high);

        while(low<high && E[low].v<=key)
            low++;
        if(low<high)
            swap(E,low,high);
    }
    E[low]=lowx;
    return low;
}

void quick_sort(Edge E[],int start,int end)
{
   int pos;
    if(start<end)
    {
        pos=fun(E,start,end);
        quick_sort(E,start,pos-1);
        quick_sort(E,pos+1,end);
    }
}
//找根节点
int find(int x)
{
    int x_root = x;
    while(f[x_root]!=-1)
        x_root = f[x_root];
    return x_root;
}
//合并两个点
int hebing(int a,int b)
{
    if( find(a) != find(b) )
    {
        f[find(b)]=find(a);
        return 1;
    }
    else
        return 0;
}
//计算点与点之间的距离
double weight(int x1,int y1,int x2,int y2)
{
    return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}
//kruskal
void L_kruskal(Edge E[])
{
    int i;
    for(i=1; i<=k; i++)
    {

        if( hebing(E[i].a,E[i].b) == 1)
        {
            hebing(E[i].a,E[i].b);
            num--;
        }
        if(num == s)break;
    }
    printf("%.2lf", E[i].v);
}

int main()
{
    int i;
    Edge E[n];
    scanf("%d%d",&s,&p);
    for(i=1; i<=p; i++)
    {
        f[i]=-1;//初始化
        scanf("%d%d",&a,&b);
        x[i]=a;
        y[i]=b;
    }
    for(i=1; i<=p; i++)
        for(int j=i+1; j<=p; j++)
        {
            k++;
            E[k].a=i;
            E[k].b=j;
            E[k].v=weight(x[i],y[i],x[j],y[j]);
        }
    quick_sort(E,1,k);
    num=p;
    L_kruskal(E);
    return 0;
}

# 拆地毯

## 题目背景

还记得 NOIP 2011 提高组 Day1 中的铺地毯吗?时光飞逝,光阴荏苒,三年过去了。组织者精心准备的颁奖典礼早已结束,留下的则是被人们踩过的地毯。请你来解决类似于铺地毯的另一个问题。

## 题目描述

会场上有 n 个关键区域,不同的关键区域由 m 条无向地毯彼此连接。每条地毯可由三个整数 u、v、w 表示,其中 u 和 v 为地毯连接的两个关键区域编号,w 为这条地毯的美丽度。

由于颁奖典礼已经结束,铺过的地毯不得不拆除。为了贯彻勤俭节约的原则,组织者被要求只能保留 K 条地毯,且保留的地毯构成的图中,任意可互相到达的两点间只能有一种方式互相到达。换言之,组织者要求新图中不能有环。现在组织者求助你,想请你帮忙算出这 K 条地毯的美丽度之和最大为多少。

## 输入格式

第一行包含三个正整数 n、m、K。

接下来 m 行中每行包含三个正整数 u、v、w。

## 输出格式

只包含一个正整数,表示这 K 条地毯的美丽度之和的最大值。

## 样例 #1

### 样例输入 #1

```
5 4 3
1 2 10
1 3 9
2 3 7
4 5 3
```

### 样例输出 #1

```
22
```

## 提示

选择第 1、2、4 条地毯,美丽度之和为 10 + 9 + 3 = 22。

若选择第 1、2、3 条地毯,虽然美丽度之和可以达到 10 + 9 + 7 = 26,但这将导致关键区域 1、2、3 构成一个环,这是题目中不允许的。


1<=n,m,k<=100000

思路

就是正常找最小生成树,美丽度相当于边长,直接求和即可

代码

#include<stdio.h>
#define N 100001

int n,m,x,y,z,k;
int f[N],sum=0,num=0;

typedef struct
{
    int a,b,v;
}Edge;
//快排
void swap(Edge E[],int low,int high)
{
    Edge temp;
    temp = E[low];
    E[low] = E[high];
    E[high] = temp;

}

int fun(Edge E[],int low,int high)
{
    int key;
    Edge lowx;
    lowx=E[low];
    key=E[low].v;
    while(low<high)
    {
        while(low<high && E[high].v>=key)
            high--;
        if(low<high)
            swap(E,low,high);

        while(low<high && E[low].v<=key)
            low++;
        if(low<high)
            swap(E,low,high);
    }
    E[low]=lowx;
    return low;
}

void quick_sort(Edge E[],int start,int end)
{
   int pos;
    if(start<end)
    {
        pos=fun(E,start,end);
        quick_sort(E,start,pos-1);
        quick_sort(E,pos+1,end);
    }
}

int find(int i)
{
   int i_root = i;
   while(f[i_root]!=-1)
    i_root = f[i_root];
   return i_root;
}

int hebing(int a,int b)
{
    if( find(a) != find(b) )
    {
        f[find(b)]=find(a);
        return 1;
    }
    else
    return 0;
}

void L_kruskal(Edge E[])
{
     for(int i=m;i>=1;i--)
    {
        if( hebing(E[i].a,E[i].b) == 1)
        {
            hebing(E[i].a,E[i].b);
            num++;
            sum+=E[i].v;
        }
        if(num == k)break;
    }

       printf("%d\n",sum);
}

int main()
{
    Edge E[N];
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&E[i].a,&E[i].b,&E[i].v);
    quick_sort(E,1,m);
    for(int i=1;i<=n;i++)
        f[i]=-1;
    L_kruskal(E);
   return 0;
}

# [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 一只书虫仔。

思路

跟上个题目没啥大的区别,先把有的节点连起来,然后就是求出每个点之间的距离,排列好,然后相加得到结果

代码

#include<stdio.h>
#include<math.h>
#define BER 1000001

int n,m,p,q,k;
double x[1001],y[1001];
int f[1001],num;
double sum=0;

typedef struct
{
    int a,b;
    double v;
}Edge;

void swap(Edge E[],int low,int high)
{
    Edge temp;
    temp = E[low];
    E[low] = E[high];
    E[high] = temp;

}

int fun(Edge E[],int low,int high)
{
    double key;
    Edge lowx;
    lowx=E[low];
    key=E[low].v;
    while(low<high)
    {
        while(low<high && E[high].v>=key)
            high--;
        if(low<high)
            swap(E,low,high);

        while(low<high && E[low].v<=key)
            low++;
        if(low<high)
            swap(E,low,high);
    }
    E[low]=lowx;
    return low;
}

void quick_sort(Edge E[],int start,int end)
{
   int pos;
    if(start<end)
    {
        pos=fun(E,start,end);
        quick_sort(E,start,pos-1);
        quick_sort(E,pos+1,end);
    }
}

int find(int i)
{
   int i_root = i;
   while(f[i_root]!=-1)
    i_root = f[i_root];
   return i_root;
}

int hebing(int a,int b)
{
    if( find(a) != find(b) )
    {
        f[find(b)]=find(a);
        return 1;
    }
    else
    return 0;
}

double weight(double x1,double y1,double x2,double y2)
{
  return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

void L_kruskal(Edge E[])
{
     for(int i=1;i<=k;i++)
    {
        if( find(E[i].a) != find(E[i].b) )
        {
            hebing(E[i].a,E[i].b);
            num++;
            sum+=E[i].v;
        }
        if(num == n-1)break;
    }
    printf("%.2lf",sum);
}

int main()
{
    Edge E[BER];
    scanf("%d%d",&n,&m);
    num=m;
    for(int i=1;i<=n; i++)
    {
        f[i]=-1;
        scanf("%lf%lf",&x[i],&y[i]);
    }
     k=0;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
    {
        k++;
        E[k].a=i;E[k].b=j;
        E[k].v=weight(x[i],y[i],x[j],y[j]);
    }
    quick_sort(E,0,k);
    while(m--)
    {
       scanf("%d%d",&p,&q);
        hebing(p,q);
    }
   L_kruskal(E);
        return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值