您的位置:首页 > 产品设计 > UI/UE

POJ 3625 Building Roads 解题报告

2009-08-04 15:36 302 查看
题意:给出N个坐标点的位置,其中的K个是已经相连的,求把它们连在一起需要最短的路径和。

分析:典型的最小生成树。一开始我选择了Kruskal(我也不知道怎么会选上的。。)。把所有有可能的边全部添加进去,外加stl的heap优化。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <map>
#include <vector>
using namespace std;
#define dis(x1,y1,x2,y2) sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
struct Edge
{
int u,v;
double w;
//由于stl的heap是最大堆,所以这里重载小于号为大于号
//就能使heap变为最小堆
bool operator<(const Edge &x) {
return w > x.w;
}
};
Edge edge[600000];
double x[1100],y[1100];
int p[1100];
//查找最高父节点
int par(int x)
{
if(p[x] < 0)
return x;
return (p[x] = par(p[x]));
}
//合并节点
int join(int x,int y)
{
int xx,yy;
xx = par(x);
yy = par(y);
if(xx != yy)
if(p[xx] < p[yy]) {
p[xx] += p[yy];
p[yy] = xx;
} else {
p[yy] += p[xx];
p[xx] = yy;
}
}
int main()
{
int n,m,last;
scanf("%d%d",&n,&m);
last = n-1;
for(int i = 1; i <= n; i++)
scanf("%lf%lf",x+i,y+i);
memset(p,-1,sizeof(p));
int xx,yy;
for(int i = 0; i < m; i++) {
scanf("%d%d",&xx,&yy);
//将一开始就连在一起的节点合并
if(par(xx) != par(yy)) {
join(xx,yy);
last--;
}
}
int pn(0);
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++) {
edge[pn].u = i;
edge[pn].v = j;
edge[pn++].w = dis(x[i],y[i],x[j],y[j]);
}
//用heap必先make_heap
make_heap(edge,edge+pn);
double res(0.0);
int u,v;
double w;
while(last)
{
u = edge[0].u;
v = edge[0].v;
w = edge[0].w;
if(par(u) != par(v)) {
join(u,v);
res += w;
last--;
}
//弹出最小值边
pop_heap(edge,edge+pn);
pn--;
}
printf("%.2f/n",res+10e-5);

return 0;
}
//8524K 125MS G++ 1456B


后来在discuss发现用朴素的prim便能达到很高的效率了,于是,再写了一个版本。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <map>
#include <vector>
using namespace std;
#define D(x1,y1,x2,y2) sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
const int N = 1100;
double dis

,x
,y
;
bool v
;
int main()
{
int n,m,u,tx,ty;
double least
,res,_min;
bool v
;
memset(v,false,sizeof(v));
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
scanf("%lf%lf",x+i,y+i);
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++)
dis[i][j] = dis[j][i] = D(x[i],y[i],x[j],y[j]);
for(int i = 0; i < m; i++) {
scanf("%d%d",&tx,&ty);
dis[tx][ty] = dis[ty][tx] = 0.0;
}
for(int i = 1; i <= n; i++)
least[i] = dis[1][i];
v[1] = true;
res = 0.0;
for(int i = 1; i < n; i++) {
_min = 0x7fffffff;
for(int j = 2; j <= n; j++)
if(!v[j] && least[j] < _min) {
_min = least[j];
u = j;
}
v[u] = true;
res += _min;
for(int j = 2; j <= n; j++)
if(!v[j] && dis[u][j] < least[j])
least[j] = dis[u][j];
}
printf("%.2f/n",res);

return 0;
}
//9312K 94MS G++ 1134B


不仅效率高了,编程复杂度还降了下来。

下次记住了:Kruskal O(E*lg(v))

Prim O(E+V*lg(V))

稠密图使用Prim,Kruskal使用在稀疏图
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: