poj1751 highways 最小生成树记录路径

题目链接:http://poj.org/problem?id=1751

题目大意:给出一些点,代表一个地方的坐标,还有给出一些已经修好的路,问还要修那几条,使得修的路的和长度最小。

分析:这道题,很明显最小生成树啊,可是有的地方已经修了,那也是最小生成树,那就在初始化完成后,让这两个地方距离为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);
        }
    }
    //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);*/
}

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();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值