利用Kruskal算法求最小生成树解决聪明的猴子问题 -- 数据结构
题目:聪明的猴子
链接:https://ac.nowcoder.com/acm/problem/19964
输入描述:
第1行为一个整数,表示猴子的个数M(2 ≤ M ≤ 500);
第2行为M个整数,依次表示猴子的最大跳跃距离(每个整数值在1--1000之间);
第3行为一个整数表示树的总棵数N(2 ≤ N ≤ 1000);
第4行至第N+3行为N棵树的坐标(横纵坐标均为整数,范围为:-1000--1000)。(同一行的整数间用空格分开)
输出描述:
包括一个整数,表示可以在这个地区的所有树冠上觅食的猴子数
输入
4
1 2 3 4
6
0 0
1 0
1 2
-1 -1
-2 0
2 2
输出
3
分析: 1.题目中的树冠上觅食的猴子数是指能够在所有树上自由移动的猴子数; 2.为了解决这一道题,我们应该要求出最小生成树中最长边的大小,再用每个猴子能够移动的最大距离逐一比较; 3.这里我将采用Kruskal算法求最小生成树:
①将所有边按权值排序(以升序为例)
②按照边的排序构建最小生成树
代码: 1.顶点结构定义:
typedef struct point{
int x, y;//坐标(x,y)
bool status;
}point;
2.边结构定义:
typedef struct edge{
point p1, p2;//边的两个端点
double weight;//边的权值
}edge;
3.对edge e[]进行升序排序:
//按权值快速排序
qsort(e, tree_num*(tree_num-)/, sizeof(edge), cmp);
//cmp函数
int cmp(const void *a, const void *b)
{
return (*(edge *)a).weight > (*(edge *)b).weight ? : -;
}
4.Kruskal函数
/*传入:
edge *e 存放边的数组
int tree_num 树的数量
point *p 存放点的数组
*/ double kruskal(edge *e, int tree_num, point *p)
{
//并查集
int v[tree_num];
for(int i=; i<tree_num; i++){
v[i] = i;
} double longest; int i1, i2;
for(int i=; i<tree_num*(tree_num-)/; i++){
if(search(p, e[i].p1, tree_num) == - || search(p, e[i].p2, tree_num) == -){
exit(-);//ERROR:没有找到e[i].p1在p[]中的坐标
}
//serach()查找 e[i].p1在p[]中的坐标
i1 = v[search(p, e[i].p1, tree_num)];
i2 = v[search(p, e[i].p2, tree_num)]; if(i1 == i2 == tree_num-)break;//已经构建成最小生成树
if(i1 == i2)continue;//边的两个端点已经在同一个集合中 //将i1,i2中较大的作为标记
int i3 = i1>i2 ? i1:i2;
for(int j=; j<tree_num; j++){
if(v[j] == i1 || v[j] == i2){
v[j] = i3;
}
}
longest = e[i].weight;//更新长边
}
return longest;//返回最长边
}
5.search函数(Kruskal函数中调用):
int search(point *p, point p1, int tree_num)
{
for(int i=; i<tree_num; i++){
if(p1.x == p[i].x && p1.y == p[i].y){
return i;
}
}
return -;
}
6.所有代码:
#include<iostream>
#include<cmath>
#include<cstring>
#include <stdlib.h>
using namespace std; #define max 1000 typedef struct point{
int x, y;//坐标(x,y)
bool status;
}point; typedef struct edge{
point p1, p2;//边的两个端点
double weight;//边的权值
}edge; int cmp( const void *a ,const void *b);
double kruskal(edge *e, int tree_num, point *p);
int get_root(int *v, int x);
int search(point *p, point p1, int tree_num);
int main(){
//输入猴子数量
int monkey_num;
cin>>monkey_num; //输入猴子能到达的最远距离
int step[monkey_num];
memset(step, , sizeof(step));
for(int i=; i<monkey_num; i++){
cin>>step[i];
} //输入树的数量
int tree_num;
cin>>tree_num; //输入树的坐标(点记录)
point p[max];
memset(p, , sizeof(p));
for(int i=; i<tree_num; i++){
cin>>p[i].x>>p[i].y;
p[i].status = ;//status为1表示可操作的树
} //边记录
edge e[max];
int t = ;
int vi[max] = {};
for(int i=; p[i].status != ; i++){
for(int j=; p[j].status != ; j++){
if(i != j && vi[j] == ){//增加边的权值,两个端点
e[t].weight = sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x) + (p[i].y-p[j].y)*(p[i].y-p[j].y));
e[t].p1 = p[i];
e[t++].p2 = p[j];
}
}
vi[i] = ;//标记p[i]点连接的所有边已经被读取
} //按权值快速排序
qsort(e, tree_num*(tree_num-)/, sizeof(edge), cmp); double longest = kruskal(e, tree_num, p); int ans = ;
for(int i=; i<monkey_num; i++){
if(step[i] >= longest)ans++;
}
cout<<ans; return ;
} int cmp(const void *a, const void *b)
{
return (*(edge *)a).weight > (*(edge *)b).weight ? : -;
} double kruskal(edge *e, int tree_num, point *p)
{
//并查集
int v[tree_num];
for(int i=; i<tree_num; i++){
v[i] = i;
} double longest; int i1, i2;
for(int i=; i<tree_num*(tree_num-)/; i++){
if(search(p, e[i].p1, tree_num) == - || search(p, e[i].p2, tree_num) == -){
exit(-);//ERROR:没有找到e[i].p1在p[]中的坐标
}
//serach()查找 e[i].p1在p[]中的坐标
i1 = v[search(p, e[i].p1, tree_num)];
i2 = v[search(p, e[i].p2, tree_num)]; if(i1 == i2 == tree_num-)break;//已经构建成最小生成树
if(i1 == i2)continue;//边的两个端点已经在同一个集合中 //将i1,i2中较大的作为标记
int i3 = i1>i2 ? i1:i2;
for(int j=; j<tree_num; j++){
if(v[j] == i1 || v[j] == i2){
v[j] = i3;
}
}
longest = e[i].weight;
}
return longest;
} int search(point *p, point p1, int tree_num)
{
for(int i=; i<tree_num; i++){
if(p1.x == p[i].x && p1.y == p[i].y){
return i;
}
}
return -;
}
ALL
总结:
很多时候还是听懂容易实践难,难就难在为了实现功能要有一层一层缜密的逻辑需要构建,漏了一种情况都会影响结果的。 (外面的oj真的很严格呢)

参考资料: 【算法】图的最小生成树(Kruskal算法)
利用Kruskal算法求最小生成树解决聪明的猴子问题 -- 数据结构的更多相关文章
- 克鲁斯卡尔(Kruskal)算法求最小生成树
/* *Kruskal算法求MST */ #include <iostream> #include <cstdio> #include <cstring> #inc ...
- Prim算法和Kruskal算法求最小生成树
Prim算法 连通分量是指图的一个子图,子图中任意两个顶点之间都是可达的.最小生成树是连通图的一个连通分量,且所有边的权值和最小. 最小生成树中,一个顶点最多与两个顶点邻接:若连通图有n个顶点,则最小 ...
- Prime算法 与 Kruskal算法求最小生成树模板
算法原理参考链接 ==> UESTC算法讲堂——最小生成树 关于两种算法的复杂度分析 ==> http://blog.csdn.net/haskei/article/details/531 ...
- kruskal算法求最小生成树(jungle roads的kruskal解法)
注意: 注意数组越界问题(提交出现runtimeError代表数组越界) 刚开始提交的时候,边集中边的数目和点集中点的数目用的同一个宏定义,但是宏定义是按照点的最大数定义的,所以提交的时候出现了数组越 ...
- Kruskal算法求最小生成树
Kruskal算法是根据权来筛选节点,也是采用贪心算法. /// Kruskal ///初始化每个节点为独立的点,他的祖先为自己本身 void made(int n) { ; i<=n; i++ ...
- 859. Kruskal算法求最小生成树(模板)
给定一个n个点m条边的无向图,图中可能存在重边和自环,边权可能为负数. 求最小生成树的树边权重之和,如果最小生成树不存在则输出impossible. 给定一张边带权的无向图G=(V, E),其中V表示 ...
- Kruskal算法求最小生成树 笔记与思路整理
整理一下前一段时间的最小生成树的算法.(其实是刚弄明白 Kruskal其实算是一种贪心算法.先将边按权值排序,每次选一条没选过的权值最小边加入树,若加入后成环就跳过. 先贴张图做个示例. (可视化均来 ...
- Kruskal算法求最小生成树(POJ2485)
题目链接:http://poj.org/problem?id=2485 #include <iostream> #include <stdio.h> #include < ...
- AcWing 859. Kruskal算法求最小生成树 稠密图
//稠密图 #include <cstring> #include <iostream> #include <algorithm> using namespace ...
随机推荐
- Java多线程和并发(五),线程的状态
目录 1.线程的六个状态 2.sleep和wait的区别 3.锁池(EntryList)和等待池(WaitSet) 4.notify和notifyall的区别 五.线程的状态 1.线程的六个状态 2. ...
- Flash大文件断点续传功能
一.概述 所谓断点续传,其实只是指下载,也就是要从文件已经下载的地方开始继续下载.在以前版本的HTTP协议是不支持断点的,HTTP/1.1开始就支持了.一般断点下载时才用到Range和Content- ...
- C/C++ - 指针 与 引用
一.指针 1.指针与指针变量的区分 a.指针:指针就是内存编号,也就是内存地址,通俗的讲,指针就是变量的地址. 注1:指针的大小是根据计算机的操作系统而定的,跟变量类型无关 注2:如果是32位的操作系 ...
- touch:创建文件及修改文件时间戳
touch 命令不光可以用来创建文件(当指定操作文件不存在时,该命令会在当前位置建立一个空文件),此命令更重要的功能是修改文件的时间参数(但当文件存在时,会修改此文件的时间参数). Linux 系统中 ...
- Confluence备份,数据迁移
一.Confluence的备份.恢复1)Confluence的备份 管理员账号登录Confluence,点击右上角的"一般配置"-"每日备份管理",如下图(默认 ...
- C++入门经典-例6.13-指针与二维数组
1:代码如下: // 6.13.cpp : 定义控制台应用程序的入口点. // #include"stdafx.h" #include<iostream> using ...
- Java程序设计第十周学习总结
Java课程知识梳理: 流的区分; 字符流与字节流的区别: 字节流是直接操作文件本身的,如果没有关闭字节流操作,文件会依然输出内容 而字符流在程序运行之后会发现文件没有任何的内容,这是因为字符流操作的 ...
- express 模板 及 文件上传
express 的三大功能: 1. 提供了静态服务(所谓的根目录) let express = require("express"); let app = express(); a ...
- docker—数据卷
启动一个数据容器并挂载本地目录 docker run -itd --name=volume /opt/volume:/tmp/volume --privileged docker.io/nginx-t ...
- vue的周期函数
beforeCreate(创建前) created(创建后) beforeMount(载入前) mounted(载入后) beforeUpdate(更新前) updated(更新后) beforeDe ...