poj1751 highways 最小生成树记录路径
2017-03-15 07:21
225 查看
题目链接:http://poj.org/problem?id=1751
题目大意:给出一些点,代表一个地方的坐标,还有给出一些已经修好的路,问还要修那几条,使得修的路的和长度最小。
分析:这道题,很明显最小生成树啊,可是有的地方已经修了,那也是最小生成树,那就在初始化完成后,让这两个地方距离为0就可以了,然后还需要记录路径。我开始是用kruskal做的,原因嘛,因为这个算法是加边的嘛,而prim是加点,但是呢,很明显这是一个稠密图,经验上来说,用prime更加贴切以一些。于是我就试了两种方法:
1,不必真正算出距离用sqrt,只需要相对距离es.cost=(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
2,注意并查集的写法,我以前写的并查集没有路径压缩,多亏了ZJ学姐指点:
int find(int x){
return par[x]==x?par[x]:par[x]=find(par[x]);
}
3,打印路径时可以在添加边时进行,不必开数组记录使用的边,等添加完后再打印。
4,另外,这个c++会TLE,g++400ms+,T到怀疑人生,如果有人知道为啥万望请一定告知!
code:
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_V=800;
const int INF=99999999;
int V,par[MAX_V],x[MAX_V],y[MAX_V],visit[MAX_V],rank[MAX_V];
struct edge{int u,v,cost;}es[MAX_V*MAX_V];
/*double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}*/
bool cmp(edge x,edge y){
return x.cost<y.cost;
}
int find(int x){
return par[x]==x?par[x]:par[x]=find(par[x]);
}
int main(void){
scanf("%d",&V);
for(int i=1;i<=V;++i)
scanf("%d%d",x+i,y+i);
int E=0;
for(int i=1;i<=V;++i)
for(int j=1;j<i;++j){
es[E].u=i;es[E].v=j;
es[E++].cost=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int N;scanf("%d",&N);
for(int i=0;i<N;++i){
int a,b;scanf("%d%d",&a,&b);
es[E].u=a;es[E].v=b;
es[E++].cost=0;
}
for(int i=0;i<=V;++i)
par[i]=i,rank[i]=0;
sort(es,es+E,cmp);
for(int i=0;i<E;++i){
edge e=es[i];
int x=find(e.u),y=find(e.v);
if(x!=y){
if(rank[x]<rank[y])par[x]=y;
else {
par[y]=x;
if(rank[x]==rank[y])++rank[x];
}
if(e.cost)printf("%d %d\n",e.u,e.v);
}
}
//print
//printf("res:\n");
/* for(int i=0;i<res;++i)
if(es[visit[i]].cost)printf("%d %d\n",es[visit[i]].u,es[visit[i]].v);*/
}
code:
#include<cstdio>
#define min(a,b) (a<b?a:b)
using namespace std;
const int MAXV=800,INF=99999999;
int cost[MAXV][MAXV],mincost[MAXV],prev[MAXV],V;
int x[MAXV],y[MAXV];
bool used[MAXV];
void prim(){
for(int i=1;i<=V;++i){
used[i]=false;
mincost[i]=INF;
prev[i]=1;
}
mincost[1]=0;
int res=0;
while(true){
int v=-1;
for(int u=1;u<=V;++u)
if(!used[u]&&(v==-1||mincost[u]<mincost[v]))v=u;
if(v==-1)break;
used[v]=true;//这一个点,prev数组记录另一个点
if(cost[v][prev[v]])printf("%d %d\n",v,prev[v]);
for(int u=1;u<=V;++u)
if(mincost[u]>cost[v][u]){
mincost[u]=cost[v][u];
prev[u]=v;//u是通过v更新的,因此prev[u]=v
}
}
}
int main(void){
scanf("%d",&V);
for(int i=1;i<=V;++i)
scanf("%d%d",x+i,y+i);
for(int i=1;i<=V;++i)
for(int j=1;j<=i;++j){
cost[i][j]=cost[j][i]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int N;scanf("%d",&N);
for(int i=0;i<N;++i){
int a,b;scanf("%d%d",&a,&b);
cost[a][b]=cost[b][a]=0;
}
prim();
}
题目大意:给出一些点,代表一个地方的坐标,还有给出一些已经修好的路,问还要修那几条,使得修的路的和长度最小。
分析:这道题,很明显最小生成树啊,可是有的地方已经修了,那也是最小生成树,那就在初始化完成后,让这两个地方距离为0就可以了,然后还需要记录路径。我开始是用kruskal做的,原因嘛,因为这个算法是加边的嘛,而prim是加点,但是呢,很明显这是一个稠密图,经验上来说,用prime更加贴切以一些。于是我就试了两种方法:
1,kruskal
这个添加的边是显然的,但要注意以下几点:1,不必真正算出距离用sqrt,只需要相对距离es.cost=(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
2,注意并查集的写法,我以前写的并查集没有路径压缩,多亏了ZJ学姐指点:
int find(int x){
return par[x]==x?par[x]:par[x]=find(par[x]);
}
3,打印路径时可以在添加边时进行,不必开数组记录使用的边,等添加完后再打印。
4,另外,这个c++会TLE,g++400ms+,T到怀疑人生,如果有人知道为啥万望请一定告知!
code:
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_V=800;
const int INF=99999999;
int V,par[MAX_V],x[MAX_V],y[MAX_V],visit[MAX_V],rank[MAX_V];
struct edge{int u,v,cost;}es[MAX_V*MAX_V];
/*double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}*/
bool cmp(edge x,edge y){
return x.cost<y.cost;
}
int find(int x){
return par[x]==x?par[x]:par[x]=find(par[x]);
}
int main(void){
scanf("%d",&V);
for(int i=1;i<=V;++i)
scanf("%d%d",x+i,y+i);
int E=0;
for(int i=1;i<=V;++i)
for(int j=1;j<i;++j){
es[E].u=i;es[E].v=j;
es[E++].cost=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int N;scanf("%d",&N);
for(int i=0;i<N;++i){
int a,b;scanf("%d%d",&a,&b);
es[E].u=a;es[E].v=b;
es[E++].cost=0;
}
for(int i=0;i<=V;++i)
par[i]=i,rank[i]=0;
sort(es,es+E,cmp);
for(int i=0;i<E;++i){
edge e=es[i];
int x=find(e.u),y=find(e.v);
if(x!=y){
if(rank[x]<rank[y])par[x]=y;
else {
par[y]=x;
if(rank[x]==rank[y])++rank[x];
}
if(e.cost)printf("%d %d\n",e.u,e.v);
}
}
//printf("res:\n");
/* for(int i=0;i<res;++i)
if(es[visit[i]].cost)printf("%d %d\n",es[visit[i]].u,es[visit[i]].v);*/
}
prim
这个算法的思想是添加点,因此需要一个pre数组来记录,上一个点,稠密图用prim明显比较快。另外考虑到路径是双向的,故a[i][j]=a[j][i]=distance,另外如果有重边还要加判断,而kruskal就没这样的顾虑,因为它是按边的长短确定添加顺序的。code:
#include<cstdio>
#define min(a,b) (a<b?a:b)
using namespace std;
const int MAXV=800,INF=99999999;
int cost[MAXV][MAXV],mincost[MAXV],prev[MAXV],V;
int x[MAXV],y[MAXV];
bool used[MAXV];
void prim(){
for(int i=1;i<=V;++i){
used[i]=false;
mincost[i]=INF;
prev[i]=1;
}
mincost[1]=0;
int res=0;
while(true){
int v=-1;
for(int u=1;u<=V;++u)
if(!used[u]&&(v==-1||mincost[u]<mincost[v]))v=u;
if(v==-1)break;
used[v]=true;//这一个点,prev数组记录另一个点
if(cost[v][prev[v]])printf("%d %d\n",v,prev[v]);
for(int u=1;u<=V;++u)
if(mincost[u]>cost[v][u]){
mincost[u]=cost[v][u];
prev[u]=v;//u是通过v更新的,因此prev[u]=v
}
}
}
int main(void){
scanf("%d",&V);
for(int i=1;i<=V;++i)
scanf("%d%d",x+i,y+i);
for(int i=1;i<=V;++i)
for(int j=1;j<=i;++j){
cost[i][j]=cost[j][i]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int N;scanf("%d",&N);
for(int i=0;i<N;++i){
int a,b;scanf("%d%d",&a,&b);
cost[a][b]=cost[b][a]=0;
}
prim();
}
相关文章推荐
- POJ 1751 Highways (最小生成树+记录路径)
- Highways---poj1751最小生成树
- POJ1751-Highways(最小生成树+并查集)
- POJ1751--Highways(最小生成树,kauskal)
- 最小生成树(不记录路径)
- POJ1751 Highways【最小生成树】
- POJ ~ 1751 ~ Highways (最小生成树 + 打印路径)
- Highways(最小生成树_prim()求最短路中的最大路径)
- POJ 1751 Highways【最小生成树+输出路径】
- poj 1751 Highways (最小生成树+记录路径)
- POJ-2485 Highways 最小生成树
- poj 2485 Highways -----最小生成树。prim
- POJ 2485 Highways(最小生成树)
- poj 2485 Highways 最小生成树
- 数据结构-图-Java实现:有向图 图存储(邻接矩阵),最小生成树,广度深度遍历,图的连通性,最短路径
- SPOJ 104 Highways(最小生成树计数)
- POJ2485 Highways【最小生成树】【哈哈,第一个首次提交就AC的最小生成树】
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- POJ 1751 Highways 最小生成树
- POJ 2485 Highways【最小生成树】