以(x,y)坐标的形式给出n个点,修建若干条路使得所有点连通(其中有两个给出的特殊点必须相邻),求所有路的总长度的最小值。
因对所修的路的形状没有限制,所以可看成带权无向完全图,边权值为两点间距离。因是稠密图,故用邻接矩阵存储更好(完全图,边数e达到n(n-1)/2)。
至此,可将问题抽象为求最小生成树的边权和。
邻接矩阵版Prim算法求最小生成树,时间复杂度为O(n*n)。邻接表版prim用堆优化后理论上可达O(elogn),边数组版kruscal理论也为O(elogn),但此题是完全图,e=n(n-1)/2,故实为O(n*nlogn)>O(n*n)。
对于所给的特殊的两个相邻点,只需在开始循环前把这两个点加入子树集合并更新它们所到达的点的mincost即可。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cmath> 4 using namespace std; 5 6 const int MAX_N=55; 7 const double INF=2000; 8 9 struct Point 10 { 11 int x,y; 12 }a[MAX_N]; 13 14 double dis(Point& p1, Point& p2) 15 { 16 return sqrt((double)(p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); 17 } 18 19 int n; 20 int nike,apple; 21 double cost[MAX_N][MAX_N];//邻接矩阵(更适于稠密图) 22 double mincost[MAX_N];//集合到点i的最短距离 23 int used[MAX_N]; 24 25 double prim() 26 { 27 double res; 28 for(int i=0;i<n;i++) 29 { 30 mincost[i]=INF; 31 used[i]=0; 32 }//将nike,apple两个点加入集合,并更新它们所到达的点的mincost 33 mincost[nike]=mincost[apple]=0; 34 used[nike]=used[apple]=1; 35 res=cost[nike][apple]; 36 for(int i=0;i<n;i++) 37 { 38 mincost[i]=min(mincost[i],cost[nike][i]); 39 } 40 for(int i=0;i<n;i++) 41 { 42 mincost[i]=min(mincost[i],cost[i][apple]); 43 } 44 while(1) 45 { 46 int v=-1; 47 for(int i=0;i<n;i++)//找到集合以外的mincost最小的点 48 { 49 if(!used[i]&&(v==-1||mincost[i]<mincost[v])) 50 v=i; 51 } 52 if(v==-1) break;//不存在负权边 53 used[v]=1; 54 res+=mincost[v];//加入集合,更新它所到达的点 55 for(int i=0;i<n;i++) 56 { 57 mincost[i]=min(mincost[i],cost[i][v]); 58 } 59 } 60 return res; 61 } 62 63 64 int main() 65 { 66 freopen("1011.txt","r",stdin); 67 while(scanf("%d",&n)!=EOF) 68 { 69 if(n==0) break; 70 scanf("%d%d",&nike,&apple); 71 nike--; 72 apple--; 73 for(int i=0;i<n;i++) 74 { 75 scanf("%d%d",&a[i].x,&a[i].y); 76 } 77 for(int i=0;i<n;i++)//求两点间距离,得到邻接矩阵 78 { 79 cost[i][i]=0; 80 for(int j=i+1;j<n;j++) 81 cost[i][j]=cost[j][i]=dis(a[i],a[j]); 82 } 83 printf("%.2lf\n",prim()); 84 } 85 return 0; 86 }
时间: 2024-10-14 05:32:43