政府审批的规则如下:
(1)如果两个或以上城市申请修建同一条公路,则让它们共同修建;
(2)如果三个或以上的城市申请修建的公路成环。如下图,A申请修建公路AB,B申请修建公路BC,C申请修建公路CA。则政府将否决其中最短的一条公路的修建申请;
(3)其他情况的申请一律同意。
题目说了一大堆,看起来就是在求最小生成树的。但是规则2有点难受,但是我们仔细想一想这个规则其实是没用的==。
看到一个dalao是这样写的
因为A申请修建公路AB,所以AB<=AC,B申请修建公路BC,所以BC<=AB,C申请修建公路CA,所以AC<=BC,而这三个条件同时成立当且仅当AB=BC=AC,既然这样,去掉那条边都是一样的,这与求解最小生成树不冲突。
所以题目就是裸的最小生成树了,但是这道题给的是各点坐标,点数又是5000级别,那么边数就是n²的水平,因为我原来只会Kruskal(太菜了),而Kruskal是基于边的算法,复杂度O(mlogm),所以会超时==。于是现补了一下Prim算法,它是基于点的算法,复杂度O(n²),所以在这道题中就比较合适。
而且如果用Kruskal的话,肯定要处理出所以边权的,而显然这样会MLE,所以这题就是Prim专属咯。
口胡一下Prim算法:
@ 算法思想:逐步扩展
> 使用类似与 Dijkstra 算法的思想,逐步确定在生成树中的点;
> 随便找一个起点,一开始只有起点是确定在生成树中的;
> 在起点的邻居中找一个边权最短的,这条边就确定在生成树中了;
> 再从没有确定的点中找一个距离确定的点中边权最小的,重复这个
过程直到所有点被确定。
@ 这个算法与 Dijkstra 的不同之处在于,找最小的时候,不再与起
点的距离,而是和所有的确定点中的最小距离。
(yl老师的课件)
所以prim算法还是要学一学的啊==
Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #include<iostream> 5 6 using namespace std; 7 const int inf=0x7ffffff; 8 9 int n,pos; 10 bool vis[6000]; 11 double ans,dis[6000]; 12 int x[6000],y[6000]; 13 14 double math_dis(int a,int b) 15 { 16 return sqrt((double)(x[a]-x[b])*(x[a]-x[b])+(double)(y[a]-y[b])*(y[a]-y[b])); 17 } 18 19 void Prim() 20 { 21 for(int i=1;i<=n;i++) 22 { 23 double sta=1e8; 24 for(int j=1;j<=n;j++) 25 if(!vis[j]&&dis[j]<sta) 26 { 27 sta=dis[j];pos=j; 28 } 29 ans+=sta; 30 vis[pos]=1; 31 for(int j=1;j<=n;j++) 32 { 33 double tmp=math_dis(pos,j); 34 // cout<<tmp<<endl; 35 if(tmp<dis[j]) dis[j]=tmp; 36 } 37 } 38 } 39 40 int main() 41 { 42 scanf("%d",&n); 43 for(int i=1;i<=n;i++) 44 { 45 scanf("%d%d",&x[i],&y[i]); 46 dis[i]=1e8; 47 } 48 dis[1]=0; 49 Prim(); 50 printf("%.2lf",ans); 51 return 0; 52 }