您的位置:首页 > 其它

poj1751 highways 最小生成树记录路径

2017-03-15 07:21 225 查看
题目链接: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();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息