题意:n个点修路,要求总长度最小,但是有两个点p、q必须相连
思路:完全图,prim算法的效率取决于节点数,适用于稠密图。用prim求解。
p、q间距离设为0即可,最后输出时加上p、q间的距离
prim算法:
#include<iostream> #include<stdio.h> #include<string.h> #include<math.h> using namespace std; #define INF 15000//计算得最长值 #define MAXN 128 bool vis[MAXN]; double lowc[MAXN]; struct Point{ double x,y; }p[MAXN]; double dis(Point a,Point b){ return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } double prim(double cost[][MAXN],int n){//标号从0开始 double ans=0,minc; int i,j,p; memset(vis,false,sizeof(vis)); vis[0]=true; for(i=1;i<n;++i)lowc[i]=cost[0][i]; for(i=1;i<n;++i){ minc=INF; p=-1; for(j=0;j<n;++j) if(!vis[j]&&lowc[j]<minc){ minc=lowc[j]; p=j; } if(minc==INF)return -1;//原图不连通 ans+=minc; vis[p]=true; for(j=0;j<n;++j) if(!vis[j]&&cost[p][j]<lowc[j]) lowc[j]=cost[p][j]; } return ans; } int main(){ int n,m,a,b,i,j; double cost[MAXN][MAXN],w; while(~scanf("%d",&n)&&n){ //m=n*(n-1)/2;//m边条数 scanf("%d%d",&a,&b); --a;--b; for(i=0;i<n;++i) scanf("%lf%lf",&p[i].x,&p[i].y); for(i=0;i<n;++i) for(j=i+1;j<n;++j){ w=dis(p[i],p[j]); if((i==a&&j==b)||(j==a&&i==b))w=0; cost[i][j]=cost[j][i]=w; } printf("%.2f ",prim(cost,n)+dis(p[a],p[b])); } return 0; }
kruskal算法的效率取决于边数,适用于稀疏图。
边数为50*50,也不是很多,也可用kruskal算法:
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> using namespace std; #define MAXN 110//最大点数 #define MAXM 10000//最大边数 int F[MAXN];//并查集使用 struct Point{ double x,y; }p[MAXN]; struct Edge{ int u,v; double w; }edge[MAXM];//存储边的信息,包括起点/终点/权值 int tol;//边数,加边前赋值为0 double dis(Point a,Point b){ return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } void addedge(int u,int v,double w){ edge[tol].u=u; edge[tol].v=v; edge[tol++].w=w; } //排序函数,将边按照权值从小到大排序 bool cmp(Edge a,Edge b){ return a.w<b.w; } int find(int x){ if(F[x]==-1)return x; return F[x]=find(F[x]); } //传入点数,返回最小生成树的权值,如果不连通返回-1 double kruskal(int n){ memset(F,-1,sizeof(F)); sort(edge,edge+tol,cmp); int cnt=0;//计算加入的边数 int i,u,v,t1,t2; double w,ans=0; for(i=0;i<tol;++i){ u=edge[i].u; v=edge[i].v; w=edge[i].w; t1=find(u); t2=find(v); if(t1!=t2){ ans+=w; F[t1]=t2; ++cnt; } if(cnt==n-1)break; } if(cnt<n-1)return -1;//不连通 return ans; } int main(){ int n,a,b,i,j; double w; while(~scanf("%d",&n)&&n){ scanf("%d%d",&a,&b); for(i=1;i<=n;++i) scanf("%lf%lf",&p[i].x,&p[i].y); tol=0; for(i=1;i<=n;++i) for(j=i+1;j<=n;++j){ w=dis(p[i],p[j]); if((i==a&&j==b)||(j==a&&i==b))w=0; addedge(i,j,w); } printf("%.2f ",kruskal(n)+dis(p[a],p[b])); } return 0; }