POJ 3013 Big Christmas Tree 手写Heap和STl对操作

来源:互联网 发布:java的splice方法 编辑:程序博客网 时间:2024/06/06 07:37

题目大意是:给定一批材料,组装成一棵圣诞树,使得总的花费最小。关于最小花费的具体规则是是:圣诞树是一颗无向树形图,其中,编号为1的节点为根节点,原始图中每条边具有边权(unit):材料的单位价值,每个点也有一个权(weight):点的重量。生成树中,各个点处的花费是指向该点的边权(unit*该点的子树中所有点的重量(weight)和,总的花费则是生成树中所有点的花费之和。

解题思路:直接根据题意来理解,很容易把它当做一个生成树的题来解,而且是一颗边权值没有直接给定的生成树:要找出生成树,即要找出生成树上各边的权,就必须先找出子树,要找出子树就必须先够着完整的树,或者构造树的时候同步更新祖先节点权。照这个思路下去。想了很长时间,不是复杂度太高就是陷入死循环,后来写成数学公式,突然想到,把子树节点重量和边权相乘的过程分离开来,以节点重量为因子重新归类,重组后可以转化为一个求根节点到所有其他节点的最短路径值*边权的和。转化后就是一个简单的最短路了,根据数据要求,采用Dijstra+动态Heap解决。

写下这个学习报告的时候我先自我检讨一下,这个题两周前就开始做了,化了一个多星期才解决,主要原因是之前没结合Dijstra写动态的heap,所以code的时候把精力放在动态heap上,但是写了很长时间总是wa,下了一个别人ac的代码来研究,他用的是priority_queue。照我的理解,他的priority_queue应该会比我的手写heap差很多,因为松弛后更新相关的路径值,用priority_queue只能从最后插入了,然后全局线性查找,复杂度是O(n2),而我手写堆直接用一个数组动态记录各点在堆里的实际位置,更新的时候直接更改堆中指定位置上的路径值然后向上调整小根堆,复杂度是O(n)。但是依然wa,于是我根据输入条件,手写了一个点数量最多100,组数为10组的随机数生成器,反复的用两份代码测试比较后发现我的代码的确输出错误。以为是手写heap的问题,于是反复的改啊改,反复的wa啊,到后来实在不知带还能怎样改了,心都凉了,先搁一隔吧。到第二周周末晚上拿出来再看,突然发现了问题,堆操作完全正确,问题出在:Dijstra函数里松弛后更新堆里的路径值时,传入的形参由dist[J]写成了dist[J]+g[K][j],由此导致了持续两周的悲剧。痛定思痛后,以后写这些优化算法的代码时,应养成以下习惯

         1.优化之前,先保证原始算法的正确性,原始算法要是都出现了问题,优化的时候可能就会发生我这样的悲剧了:反复的在优化上面耗磨,最终的问题却在最基础的东西上,内流满面啊。2.写好并确定原始算法的正确性后要把原始算法单独存一份,优化完成后,可用原始代码生成答案和优化后的算法比对,验证其算法的正确性。

         改正以后果然ac,时间610ms,完成后去图书馆借了侯捷的《STl源码分析》,专门研究了一些priority_queuealgorithm里的heap函数,思路果然无出其右。于是又依据他们各写了一份,同样ac,时间比我手写的要多上100ms左右,不过两种stl对操作方法的执行时间倒是相差无几,让我有点疑惑,因为根据在网上查的一些资料,多数据下priority_queue执行效率相对手写的heapalgrithm里的heap函数是大大不如的,可能是oj测试数据较弱的缘故吧。

         关于比较关键的更新堆里面节点权值的这一操作,重点说一下。

         使用Dijstra算法,确定一个扩展节点后,用所有和该节点相关联的非生成树中点对应的边来更新各点的当前最短路径值,在priority_queuealgrithm里面的堆操作中都不存在直接对堆里指定的位置上进行修改后调整堆的策略,解决方法有以下几种(我所想到的):

1.       手写heap,用一个全局数组,生成从生成树节点编号到对里面数组下标的一个映射,每次对操作中,堆中元素发生变化时同步更新映射关系,更新时直接通过该映射数组找到需要修改的元素修改,然后向上调整。

2.       手写heap,但是不需要映射,更新时直接线性遍历堆,找到指定元素,然后调整,但是明显比第一种方法查找效率低,两种方法生成代码运行时间是100ms+

3.       使用priority_queue或者heap函数,更新权值的时候直接插入新元素,调整后,对于同一节点对应的堆中元素,新插入的显然权值较小,排序也靠前,因此,只需在获取并删除最小元素时,判断一下,该元素若为生成树中的点则抛弃,继续获取和删除最小元素,反之则松弛,两种方法执行效率相差不大。另外,使用heap函数时需要注意的是,函数中接受的数组范围是堆操作首地址到抹地址的下一位。

 

各种方案代码如下

手工heap

#include<stdio.h>

#include<memory.h>

typedef unsigned long long ut_64;

#define Clear(a) memset(a,0,sizeof(a))

#define N 50010

const ut_64 M=(ut_64)(1)<<63;

struct Edge

{

    int v;

    ut_64 unit;

    Edge *link;

}D[N],*E[N],mempool[N*2];

ut_64 dist[N],weight[N];

int n,m,memh;

int top,index[N];

void InsertEdge(int u,int v,ut_64 unit)

{

    Edge *e=&mempool[memh++];

    e->v=v;

    e->unit=unit;

    e->link=E[u];

    E[u]=e;

}

void InitData()

{

    int i,u,v;

    ut_64 unit;

    top=memh=0;

    scanf("%d%d",&n,&m);

    Clear(E);

    Clear(index);

    for(i=1;i<=n;i++)

        scanf("%I64u",&weight[i]);

    for(i=1;i<=m;i++){

        scanf("%d%d%I64u",&u,&v,&unit);

        InsertEdge(u,v,unit);

        InsertEdge(v,u,unit);

    }

}

 

void fix_down_heap(int i)

{

    Edge tmp=D[i];

         int j=i<<1;

         while ( j<=top )          {

                   if ( j+1<=top&&D[j].unit>D[j+1].unit )

                            j++;

                   if ( tmp.unit>D[j].unit )     {

            D[i]=D[j];

            index[D[j].v]=i;

                            i=j;

                            j=i<<1;

                   }

                   else  break;

         }

         D[i]=tmp;

         index[tmp.v]=i;

}

void fix_up_heap(int i)

{

    Edge tmp=D[i];

         int j=i>>1;

         while ( i>1 )       {

                   if ( tmp.unit<D[j].unit )     {

                            D[i]=D[j];

                            index[D[j].v]=i;

                            i=j;

                            j=i>>1;

                   }

                   else  break;

         }

         D[i]=tmp;

         index[tmp.v]=i;

}

int pop_heap()

{

    int s=D[1].v;

    D[1]=D[top--];

    index[D[1].v]=1;

    fix_down_heap(1);

    index[s]=-1;

    return s;

}

int empty_heap()

{

    if(!top)    return 1;

    else    return 0;

}

void updata_heap(int v,ut_64 unit)

{

    if(index[v]){

        D[index[v]].unit=unit;

        fix_up_heap(index[v]);

        return ;

    }

    index[v]=++top;

    D[top].v=v;

    D[top].unit=unit;

    fix_up_heap(top);

}

ut_64 dijstra(int s)

{

    int i,k;

    ut_64 sum=0;

    for(i=1;i<=n;i++)

        dist[i]=M;

         dist[s]=0;

         updata_heap(s,0);

    while(!empty_heap()){

        k=pop_heap();

        for(Edge *e=E[k];e;e=e->link)

            if(index[e->v]>=0&&dist[k]+e->unit<dist[e->v]){

                dist[e->v]=dist[k]+e->unit;

                updata_heap(e->v,dist[e->v]);

            }

    }

         for(i=1;i<=n;i++){

             if(dist[i]==M)

            return M;

                   sum+=dist[i]*weight[i];

         }

    return sum;

}

 

int main()

{

    int cs;

    ut_64 ans;

    freopen("data.txt","r",stdin);

   // freopen("my.txt","w",stdout);

    for(scanf("%d",&cs);cs;cs--){

        InitData();

        ans=dijstra(1);

        if(ans>=M)

               printf("No Answer/n");

          else

               printf("%I64u/n",ans);

    }

    return 0;

}

 

Priority_queue

#include<stdio.h>

#include<memory.h>

#include<queue>

using namespace std;

typedef unsigned long long ut_64;

#define Clear(a) memset(a,0,sizeof(a))

#define N 50010

const ut_64 M=(ut_64)(1)<<63;

struct Edge

{

    int v;

    ut_64 unit;

    Edge *link;

}*E[N],mempool[N*2];

ut_64 dist[N],weight[N];

int n,m,memh;

int used[N];

void InsertEdge(int u,int v,ut_64 unit)

{

    Edge *e=&mempool[memh++];

    e->v=v;

    e->unit=unit;

    e->link=E[u];

    E[u]=e;

}

bool operator <(Edge t1,Edge t2)

{

    return t1.unit>t2.unit;

}

void InitData()

{

    int i,u,v;

    ut_64 unit;

    memh=0;

    scanf("%d%d",&n,&m);

    Clear(E);

    for(i=1;i<=n;i++)

        scanf("%I64u",&weight[i]);

    for(i=1;i<=m;i++){

        scanf("%d%d%I64u",&u,&v,&unit);

        InsertEdge(u,v,unit);

        InsertEdge(v,u,unit);

    }

}

priority_queue<Edge,vector<Edge> > Q;

ut_64 dijstra(int s)

{

    int i,k;

    Edge D;

    ut_64 sum=0;

    for(i=1;i<=n;i++)

        dist[i]=M;

         dist[s]=0;

         Clear(used);

    D.v=s;

    Q.push(D);

    while(!Q.empty()){

        k=Q.top().v;

        Q.pop();

        if(!used[k]){

            used[k]=1;

            for(Edge *e=E[k];e;e=e->link)

                if(!used[e->v]&&dist[k]+e->unit<dist[e->v]){

                    dist[e->v]=dist[k]+e->unit;

                    D.v=e->v;

                    D.unit=dist[e->v];

                    Q.push(D);

                }

        }

    }

         for(i=1;i<=n;i++){

             if(dist[i]==M)

            return M;

                   sum+=dist[i]*weight[i];

         }

    return sum;

}

 

int main()

{

    int cs;

    ut_64 ans;

    freopen("data.txt","r",stdin);

    //freopen("my.txt","w",stdout);

    for(scanf("%d",&cs);cs;cs--){

        InitData();

        ans=dijstra(1);

        if(ans>=M)

               printf("No Answer/n");

          else

               printf("%I64u/n",ans);

    }

    return 0;

}

 

Heap函数:

#include<stdio.h>

#include<memory.h>

#include<queue>

using namespace std;

typedef unsigned long long ut_64;

#define Clear(a) memset(a,0,sizeof(a))

#define N 50010

const ut_64 M=(ut_64)(1)<<63;

struct Edge

{

    int v;

    ut_64 unit;

    Edge *link;

}D[N],*E[N],mempool[N*2];

ut_64 dist[N],weight[N];

int n,m,memh;

int top,used[N];

void InsertEdge(int u,int v,ut_64 unit)

{

    Edge *e=&mempool[memh++];

    e->v=v;

    e->unit=unit;

    e->link=E[u];

    E[u]=e;

}

int cmp(Edge t1,Edge t2)

{

    return t1.unit>t2.unit;

}

void InitData()

{

    int i,u,v;

    ut_64 unit;

    memh=0;

    scanf("%d%d",&n,&m);

    Clear(E);

    for(i=1;i<=n;i++)

        scanf("%I64u",&weight[i]);

    for(i=1;i<=m;i++){

        scanf("%d%d%I64u",&u,&v,&unit);

        InsertEdge(u,v,unit);

        InsertEdge(v,u,unit);

    }

}

 

ut_64 dijstra(int s)

{

    int i,k;

    ut_64 sum=0;

    for(i=1;i<=n;i++)

        dist[i]=M;

         dist[s]=0;

         top=1;

         Clear(used);

    D[top++].v=s;

    while(top>1){

        pop_heap(&D[1],&D[top],cmp);

        k=D[top-1].v;

        top--;

        if(used[k])  continue;

        used[k]=1;

        for(Edge *e=E[k];e;e=e->link)

            if(!used[e->v]&&dist[k]+e->unit<dist[e->v]){

                dist[e->v]=dist[k]+e->unit;

                D[top].v=e->v;

                D[top++].unit=dist[e->v];

                push_heap(&D[1],&D[top],cmp);

            }

    }

         for(i=1;i<=n;i++){

             if(dist[i]==M)

            return M;

                   sum+=dist[i]*weight[i];

         }

    return sum;

}

 

int main()

{

    int cs;

    ut_64 ans;

    freopen("data.txt","r",stdin);

    //freopen("my.txt","w",stdout);

    for(scanf("%d",&cs);cs;cs--){

        InitData();

        ans=dijstra(1);

        if(ans>=M)

               printf("No Answer/n");

          else

               printf("%I64u/n",ans);

    }

    return 0;

}

原创粉丝点击