[Usaco2007 Dec]Building Roads 修建道路

题目描述

Farmer John最近得到了一些新的农场,他想新修一些道路使得他的所有农场可以经过原有的或是新修的道路互达(也就是说,从任一个农场都可以经过一些首尾相连道路到达剩下的所有农场)。有些农场之间原本就有道路相连。 所有N(1 <= N <= 1,000)个农场(用1..N顺次编号)在地图上都表示为坐标为(X_i, Y_i)的点(0 <= X_i <= 1,000,000;0 <= Y_i <= 1,000,000),两个农场间道路的长度自然就是代表它们的点之间的距离。现在Farmer John也告诉了你农场间原有的M(1 <= M <= 1,000)条路分别连接了哪两个农场,他希望你计算一下,为了使得所有农场连通,他所需建造道路的最小总长是多少。

输入格式

第1行: 2个用空格隔开的整数:N 和 M

第2..N+1行: 第i+1行为2个用空格隔开的整数:X_i、Y_i * 第N+2..N+M+2行: 每行用2个以空格隔开的整数i、j描述了一条已有的道路, 这条道路连接了农场i和农场j

输出格式

第1行: 输出使所有农场连通所需建设道路的最小总长,保留2位小数,不必做 任何额外的取整操作。为了避免精度误差,计算农场间距离及答案时 请使用64位实型变量


很容易想到这是最小生成树的题,只不过多了一个限制:有m条已连接的边。

但是也很容易求解,而且有两种容易想到的方法:

1.把m条已连接的边看成:m条长度为0的边。修改边权后排序,敲个最小生成树模板即可。修改边权可以用map来做,由于一共有N * (N-1)/2条边,所以复杂度为:

[O(Mlogfrac{N(N-1)}{2})approx{O(MlogN^2)} ]

最小生成树可以用Prim+Heap或者Kruskal做,所以时间复杂度总共为:

[O((N+M)logfrac{N(N-1)}{2})approx{O((N+M)logN^2)} ]

或者:

[O((M+frac{N(N-1)}{2})logfrac{N(N-1)}{2})approx{O((M+N^2)logN^2)} ]

虽然看上去不一样,其实两种都是差不多的。

2.做Kruskal时,我们会从小到大枚举每条边,如果边上的两端点不在一个集合中就选中这条边。受启发于此,我们可以把每条已连接的边的两个端点都用并查集合并到一个集合中。由于Kruskal中的并查集操作复杂度非常小(因为在不停地路径压缩),所以这里把它忽略掉。那么总共的时间复杂度就是:

[O(M+frac{N(N-1)}{2}logfrac{N(N-1)}{2})approx{O(M+N^2logN^2)} ]

还是挺不错的

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define maxn 1001
#define maxm 1000001
using namespace std;

struct edge{
    int u,v; double w;
    edge(){}
    edge(const int &_u,const int &_v,const double &_w){ u=_u,v=_v,w=_w; }
    bool operator<(const edge &e)const{ return e.w-w>1e-9; }
}e[maxm];

int fa[maxn];
int x[maxn],y[maxn];
int n,m,k;
double ans;

inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}

int get(int x){
	if(fa[x]==x) return x;
	return fa[x]=get(fa[x]);
}

int main(){
    n=read(),m=read();
    for(register int i=1;i<=n;i++) x[i]=read(),y[i]=read();
    for(register int i=1;i<n;i++){
        for(register int j=i+1;j<=n;j++)
			e[++k]=edge(i,j,(double)sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(double)(y[i]-y[j])*(y[i]-y[j])));
    }

    sort(e+1,e+1+k);
    for(register int i=1;i<=n;i++) fa[i]=i;
    for(register int i=1;i<=m;i++){
        int u=read(),v=read();
        fa[get(u)]=get(v);
    }
    for(register int i=1;i<=k;i++){
        int u=get(e[i].u),v=get(e[i].v);
        if(u==v) continue;
        fa[u]=v,ans+=e[i].w;
    }
    printf("%.2lf
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/akura/p/10853388.html