首页 > 其他分享 >P4047 [JSOI2010]部落划分 题解

P4047 [JSOI2010]部落划分 题解

时间:2022-11-20 13:58:36浏览次数:80  
标签:JSOI2010 Code const int 题解 P4047 num double find

最小生成树做法

之 Kruskal

算法流程(详细分析请看代码注释):

1. 初始化并查集

并查集模板不过多解释,还不懂请参阅并查集 - OI Wiki

每个节点的祖先最开始都是自己。

还有 $\rm{find}$ 函数也一并写了。

int p[N];		//p数组存的是每个节点的祖先,N 即数据范围,保证能存下所有数据
inline int find(int x){	//习惯加上 inline
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
//...此处省略中间部分
for(int i = 1; i <= n; ++i) p[i] = i;	//每个节点的祖先初始化为自己

2. 读入每个点的坐标

用两个数组 x [ ] , y [ ] x[], y[] x[],y[] 表示每个点的横纵坐标即可。

Code
int x[N], y[N];
//...此处省略
for(int i = 1; i <= n; ++i) scanf("%d%d", &x[i], &y[i]);

3. 算出每两个点之间的距离

众所周知, 两点 A ( x 1 , y 1 ) A(x_1, y_1) A(x1​,y1​) , B ( x 2 , y 2 ) B(x_2, y_2) B(x2​,y2​) 之间的距离计算公式是:

d i s = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 dis = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2} dis=(x1​−x2​)2+(y1​−y2​)2​

So, 用一个函数来算,代码如下。

Code
inline double get_dis(int x, int y, int _x, int _y){
    //因为定义 y1 容易引起冲突,所以个人习惯命名成这样 
    return sqrt((double)(x - _x) * (x - _x) + (double)(y - _y) * (y - _y));
}

4. 只存边,用一个结构体即可搞定。

Code
int cnt;		//cnt 表示边的数量
struct Edge{
    int a, b;
    double w;
    //表示在 部落a 与 部落b 之间的距离为 w ,也就是 a, b 之间有一条权值为 w 的边
    //注意 w 是 double 类型
    bool operator < (const Edge &A) const{	//由于需要排序,先重定义一下 <
        return w < A.w;
    }
}e[M];
//...此处省略
for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= n; ++j)
        e[++cnt] = {i, j, get_dis(x[i], y[i], x[j], y[j])};
		//在点 i, j 之间创建一条边

5. 对边进行排序,直接调用 s o r t sort sort 函数 (偷懒的喜悦)

Code
sort(e + 1, e + cnt + 1);	//应该没什么好说的了...

6. Kruskal核心部分

简单说一下,更详细了解可参阅最小生成树 - OI Wiki

定义 n u m num num 表示当前部落数,初始化为 n n n。

从小到大依次枚举每条边。

如果两端点不在一个集合内,就将他们合并,同时 n u m − − num-- num−−。

如果合并完后发现 n u m num num 与 k k k 相等了,就输出下一条端点需要合并的边的长度(权值)。

Code
int num = n;
for(int i = 1; i <= cnt; ++i){
    int pa = find(e[i].a), pb = find(e[i].b);
    //找祖先判断是否在同一集合内
    if(pa != pb){	//不在一个集合内
        if(num == k){
            printf("%.2lf\n", e[i].w);	//保留两位小数
            //由于要输出下一条,就直接把判断放到最前面就可以了。
            return 0;
        }
        p[pa] = pb;	//合并
        --num;		//部落数--   (习惯写成 --num 的形式)
    }
}

Finally——完整 Code

#include <cmath>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 1010, M = N * N;
//数据范围 10^3,M 是边的个数,最大为 N * N

int n, k;
int x[N], y[N];
int p[N], cnt;
struct Edge{
    int a, b;
    double w;
    bool operator < (const Edge &A) const{
        return w < A.w;
    }
}e[M];

inline int find(int x){
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

inline double get_dis(int x, int y, int _x, int _y){
    return sqrt((double)(x - _x) * (x - _x) + (double)(y - _y) * (y - _y));
}

int main(){
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; ++i) p[i] = i;
    for(int i = 1; i <= n; ++i) scanf("%d%d", &x[i], &y[i]);
    
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)
            e[++cnt] = {i, j, get_dis(x[i], y[i], x[j], y[j])};
    
    sort(e + 1, e + cnt + 1);
    
    int num = n;
    for(int i = 1; i <= cnt; ++i){
        int pa = find(e[i].a), pb = find(e[i].b);
        if(pa != pb){
            if(num == k){
                printf("%.2lf\n", e[i].w);
                return 0;
            }
            p[pa] = pb;
            --num;
        }
    }
    return 0;
}

吸氧 135 m s 135ms 135ms

AC

蒟蒻写的很详细了。

请多多支持。

Thanks

标签:JSOI2010,Code,const,int,题解,P4047,num,double,find
From: https://www.cnblogs.com/ZZM-248/p/16908338.html

相关文章

  • U255813 争宠题解
    题目传送门#include<bits/stdc++.h>usingnamespacestd;voidjia(strings1,strings2){boolaaa=0;inta[5010],b[5010],c[5010];memset(a,0,sizeof(......
  • AtCoder Beginner Contest 278题解
    A-Shift题意给一个长度为\(n\)的数组,有\(k\)次操作,每次操作会将数组最前面的元素删掉,再在数组最后面加上一个0元素,问\(k\)次操作后的数组中的数字。思路看\(n\)与\(k......
  • Codeforces 704 B Antman 题解 (dp,贪心,结论)
    题目链接这题两种不同做法,普通的\(O(n^2)\)和奇怪的\(O(nlogn)\)。如果用\(O(nlogn)\)的话可以加强到1e6。做法1时间复杂度\(O(n^2)\)先把最终的排列随便画一个出来观......
  • C. Sum of Substrings题解
    C.SumofSubstrings题目大概意思,给你一个01串,求和最小,其中和是该串所有相邻字符所组成的十进制数的和。如:0110,sum=01+11+10=22。通过观察我们可以发现,除了第......
  • cf796部分题解
    C.ManipulatingHistory题意:给出一些字符串,有原始串(只含一个字符的串)、被替换的串、替换串、最终串(最后一行),求原始串。2aabbcdacdInitiallysis"a".Inthe......
  • B - Bracket Sequence题解
    B-BracketSequence思路:用一个flag来标记括号的数目,如果括号数目是个偶数的话,就代表当前要执行'+'操作,反之就是'*'操作。对于最外层的数,是没有计算的。所以最后要单独......
  • Auxiliary Set题解
    FAuxiliarySet树上LCA+DFS注意一下输出格式!#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+10;intt,n,q,ans;intfa[N];//存储点i的......
  • 广东工业大学第十六届程序设计竞赛题解(部分)
    E爬塔方法一:二分做法预处理每个点所能到达的最远距离,存到vector里边,然后二分处理结果#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+10;intn,......
  • F - Subarrays题解
    F-Subarrays题意:给你一个序列,问这个序列里有多少个子串的和能被k整除。思路:求前缀和,然后每个位置对k取模,模数相等的位置之间,是一个满足条件的字串。因为求的是前缀和,......
  • G water testing题解
    Gwatertesting题意:给你一个多边形(可能是凸多边形,也可能是凹多边形),问该多边形内有多少个整数点(不包含边界)。思路:皮克定理+叉乘计算三角形面积:皮克定理是指一个计算点......