poj2349-kruskal

来源:互联网 发布:通达软件下载 编辑:程序博客网 时间:2024/05/22 21:24

题意:

n个站点,s个卫星系统,每个卫星系统只能安排在一个站点
有卫星系统的站点间通讯不需要代价
任意两点(i, j)间皆可通讯,代价为dis[i][j]
找到最小的代价使得任意两个站点间均可以通讯
n, s <= 1000

kruskal

1、把所有边按从小到大的顺序排序,遍历每条边(u,v)
2、u和v在同一个连通分量中,那么加入(u,v)会成环,不能选择。
3、如果u和v在不同的连通分量,那么(u,v)一定是最优的。 用反证法证。

#include <iostream>  #include <cstdio>  //题意  注意 是除了删掉的点以外  最大的边#include <cstring>#include <algorithm>#include <cmath>using namespace std;const int maxn = 500005; //边的个数int u[maxn], v[maxn];double w[maxn], ans[maxn]; //分别代表第i条边的两个端点序号,和权值int pr[maxn]; // 找父节点int r[maxn]; //排序后第i小的边的序号保存在r[i]中int s, p, m, k; //p个点 m条边struct Pos{    int x, y;}pos[maxn];double getdis(int i, int j){    return sqrt((pos[i].x-pos[j].x)*(pos[i].x-pos[j].x) + (pos[i].y-pos[j].y)*(pos[i].y-pos[j].y));}bool cmp(const int i, const int j){    return (w[i] < w[j]);}int Find(int x){    return pr[x] == x ? x : pr[x] = Find(pr[x]);}void kruskal()  // 如果u,v 在不同的连通分量,那么加入(u,v)一定是最优的{    for(int i=0; i<p; i++) //初始化并查集        pr[i] = i;    for(int i=0; i<m; i++) //初始化边序号        r[i] = i;    sort(r, r+m, cmp);    k=0;    for(int i=0; i<m; i++)    {        int e = r[i];        int x = Find(u[e]); //当前边的端点所在的集合        int y = Find(v[e]);        if(x != y)        {            ans[k++] = w[e];            pr[x] = y;        }    }}int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%d%d",&s,&p);        m = 0;        for(int i=0; i<p; i++)        {            scanf("%d%d",&pos[i].x, &pos[i].y);        }        for(int i=0; i<p-1; i++)        {            for(int j=i+1; j<p; j++)            {                u[m] = i;                v[m] = j;                w[m++] = getdis(i,j);            }        }        kruskal();        sort(ans, ans+k-1);        printf("%.2f\n",ans[k-s]);    }    return 0;}
原创粉丝点击