题目:https://www.luogu.org/problemnew/show/P1265
分析:
本质是求最小生成树。对于规则2,构成环则申请时去掉最大边。
因为最多有5000个点,又是完全图,故不采用Kruskal算法。
用Prim算法时,用邻接矩阵则会MLE。只有涉及到松驰点的权值时,再计算边长。
易错点:
计算两点间距离时,因为点的坐标是整数,因此要将坐标平方进行类型强制转换。
double value(int i,int j){
return sqrt( (double)(x[i]-x[j])*(x[i]-x[j]) + (double)(y[i]-y[j])*(y[i]-y[j]) );
}
也可以写成:
double value(int i,int j){
return sqrt(1.0*(x[i]-x[j])*(x[i]-x[j]) + 1.0*(y[i]-y[j])*(y[i]-y[j]) );
}
注:
1.0*10,结果是double型的10;
但10*1.0,结果什么呢?
如果写成:
double value(int i,int j){
return sqrt( (x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]) );
}
则卡掉9个点。
AC代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int m,x[5001],y[5001];
int vis[5001];
double dis[5001],dist;
double ans=0;
double value(int i,int j){
return sqrt( (double)(x[i]-x[j])*(x[i]-x[j]) + (double)(y[i]-y[j])*(y[i]-y[j]) );
}
void prim(){
for(int i=1;i<=m;i++)dis[i]=1e20;
dis[1]=0;
int V=1;
for(int i=1;i<=m;i++){
double minn=1e20;
for(int j=1;j<=m;j++)
if(!vis[j])
if(minn>dis[j]){
minn=dis[j];
V=j;
}
vis[V]=1;
ans+=dis[V];
for(int j=1;j<=m;j++)
if(!vis[j]){
dist=value(V,j);
if(dis[j]>dist)dis[j]=dist;
}
}
}
int main(){
cin>>m;
for(int i=1;i<=m;i++)cin>>x[i]>>y[i];
prim();
printf("%.2f\n",ans);
return 0;
}