题目链接:https://vjudge.net/problem/POJ-2349
题意:
题目就是要我们找到一个最小的值D,把图里面所有大于D的边去掉之后剩余的连通分支的数量为S。这个就是找这个图里面最小生成树的第S大的边(也就是第n-S小的边),我们可以依照求最小生成树的过程来,首先对于每一个单点,我们把它看成一个点集,这样一开始我们有n个点集,然后我们找一条距离最小的边,合并边连接的两个点集,这样集合总数就变成了n-1个,假设我们要找一个最小的D,去掉大于D的边,使得连通分支数位n-1,那么这个最小D值就是最小的边的权值;依此类推,我们找到第二小的边(并且边连接两个点不在一个集合里面),连接边两边的点集,这样连通分支数就变成了n-2个,这条边的权值就是一个最小的D,去掉大于D后连通分支数变成了n-2个。然后我们要使得连通分支数为S,那么我们就找最小生成树里面第n-S小的边。
代码:
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 1005 /*struct point{ int u,w; }; bool operator <(const point &s1,const point &s2) { if(s1.w!=s2.w) return s1.w>s2.w; else return s1.u>s2.u; }*/ int n,m,k,t,pre[505]; struct EDGE{ int u,v; double w; }edge[300000]; struct node{ double x,y; }dot[505]; double cal_dis(int a,int b){//去a,b两点距离 return sqrt((dot[a].x-dot[b].x)*(dot[a].x-dot[b].x)+(dot[a].y-dot[b].y)*(dot[a].y-dot[b].y)); } bool cmp(EDGE s1,EDGE s2){//排序 return s1.w<s2.w; } int find(int a){ if(pre[a]==a) return a; return pre[a]=find(pre[a]); } int main() { scanf("%d",&t); while(t--){ scanf("%d%d",&m,&n); for(int i=1;i<=n;i++) scanf("%lf%lf",&dot[i].x,&dot[i].y); int cnt=0; for(int i=1;i<=n;i++) pre[i]=i; for(int i=1;i<n;i++){ for(int j=i+1;j<=n;j++){ ++cnt; edge[cnt].u=i; edge[cnt].v=j; edge[cnt].w=cal_dis(i,j); } } sort(edge+1,edge+cnt+1,cmp); double ans; m=n-m; for(int i=1;m;i++){//找最小生成树里面第m小的边 int u=edge[i].u; int v=edge[i].v; double w=edge[i].w; int x=find(u); int y=find(v); if(x!=y){ m--; pre[x]=y; if(!m){ ans=w; break; } } } printf("%.2lf\n",ans); } return 0; }