Luogu P1265修复公路【Prim最小生成树】By cellur925

题目传送门

政府审批的规则如下:

(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

 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 }
View Code

猜你喜欢

转载自www.cnblogs.com/nopartyfoucaodong/p/9665130.html