跑步 | ||||||
| ||||||
Description | ||||||
Alice终于挨到放假了,他发现自己体力下降了不少。T_T这怎么办,跑步呗。 可是Alice是女生嘛,所以耐久力比较小。不过,她知道自己一口气最多能跑X米!!!然后就会累趴下T_T。 这时候如果有Bob的鼓励的话Alice就满血满魔原地复活了。 现在的情况就是,Bob放了N个标志物(2<=N<=500,每个标志物的位置都不一样),Alice需要选择一个标志物作为她的起点。 每次她会选择一个目标,然后如果可以的话她一口气跑过去。 Bob一定会在那个标志物那里等她,并给她一个鼓励。满血满魔原地复活之后,她将又要开始新的征程。 现在问题来了,她需要经过M个不同的标志物(2<=M<=N,初始位置算是第一个)(每个标志物可以经过多次)。在她自己选择起点的情况下,她一口气最少需要跑多少米才能完成任务? | ||||||
Input | ||||||
第一行是一个整数T代表数据组数。(T<= 20) 第二行是两个整数 N 、M,代表标志物的个数(N <= 500)和她最少需要跑几个标志物 以下N行是两个整数(X,Y)代表标志物的坐标(-1000000<=X,Y<=1000000) | ||||||
Output | ||||||
对于每组输出一个小数,保留到小数点后4位(最后一位四舍五入),代表Alice最少一口气跑多少米。 样例输入 | ||||||
Sample Input | ||||||
1 3 3 1 1 1 2 1 3 | ||||||
Sample Output | ||||||
1.0000 | ||||||
Hint | ||||||
她选择(1,2)作为起点,跑到(1,1)之后Bob鼓励她一下,再跑到(1,2)Bob鼓励她一下,再跑到(1,3)Bob在鼓励她一下。 这样她一次跑1.0000m就可以顺利完成任务啦。 | ||||||
Author | ||||||
陈禹@HRBUST |
思路:
1、首先将任意两点间距离求出来,并且记录在结构体中。
2、和克鲁斯卡尔算法一样,将这些条边按照边权值从小到大排序。然后将边贪心入树,维护一个数组ji【i】,表示以i为祖先的集合中一共有多少个元素(节点),一直处理到某个集合中的元素的个数大于等于M了的时候跳出,输出最后一条入树的边即可。
3、数据比较大,在计算两点间距离的时候要注意一下。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
struct node
{
int u,v;
double w;
}a[500*500*20];
int ji[5000];
int f[5000];
int x[5000];
int y[5000];
int n,kk;
double dis(int i,int j)
{
double a=(x[i]-x[j]);
double b=(y[i]-y[j]);
return sqrt(a*a+b*b);
}
int cmp(node a,node b)
{
return a.w<b.w;
}
int find(int a)
{
int r=a;
while(f[r]!=r)
r=f[r];
int i=a;
int j;
while(i!=r)
{
j=f[i];
f[i]=r;
i=j;
}
return r;
}
int merge(int a,int b)
{
int A,B;
A=find(a);
B=find(b);
if(A!=B)
{
f[B]=A;
ji[A]+=ji[B];
}
return ji[A];
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&kk);
for(int i=1;i<=n;i++)f[i]=i,ji[i]=1;
for(int i=0;i<n;i++)
{
scanf("%lld%lld",&x[i],&y[i]);
}
int cont=0;
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
a[cont].u=i+1;
a[cont].v=j+1;
a[cont++].w=dis(i,j);
}
}
int tmp=0;
sort(a,a+cont,cmp);
for(int i=0;i<cont;i++)
{
int sum=merge(a[i].u,a[i].v);
if(sum>=kk)
{
printf("%.4lf\n",a[i].w);
break;
}
}
}
}