poj 3847 树(链接表与堆栈的实现)

来源:互联网 发布:unity3d 上海 外包 编辑:程序博客网 时间:2024/06/03 17:12

Moving to Nuremberg

http://poj.org/problem?id=3847

树中给定各边长及每点要到达的次数。求最优的点,满足所走路径和最小,要求:使得从该点开始访问其他点的次数符合给定要求并且每次只能访问一个点并返回。

思路:

任何一条边可以把树分成两颗子树,假定我们知道了一颗子树需要访问的次数和accessnum,则另一颗访问和是剩下的部分。于是该边上两点啊a,b若选为目标点的花费cost(x)存在关系:cost(b)-cost(a)=accessnum(a)*weight-accessnum(b)*weight[即:选b和选a的差别在于a树的点要过该边而b树的点不用过]。

于是我们可以从叶子节点开始扩展子树,有边<a,b>则,accessnum(b)+=accessnum(a),同时可以求出从b点访问a的子树木的代价cost'(b)+=cost'(a)+accessnum(a)*weight(a,b),直到扩展完整棵树木,此时的cost'(x)就是根的代价cost,其他的都不是。然后根据上一段地公式回溯求出叶子节点的cost(x)。

 

数据结构:这个题目的时间限制很严格,开始用递归调用严重超时。另外递归调用vc编译只能处理深度超过1万多的,然后就终止。后改用系统的vector存储边/queue或stack跟踪再次超时了。因而需要我们用链接表来存储边,并且实现数组堆栈,以能在规定时间内运行完。此外,数据大要用64位,真是麻烦~

 

struct Edge{
    int b,t;
    Edge *next;
}*edge[NUM];

//扩展树,先找叶子节点,cost(a)=cost(b)+lost(a)

        memset(flag,true,sizeof(flag)); //标记是否被扩展或者回溯
        memset(cost,0,sizeof(cost));

        qn = 0;
        for(i = 1;i <= n;i ++)
        {
            if(size[i] == 1)
                que[qn++] = i;
        }
        int x = -1;
        while(qn > 0)
        {
            x = que[--qn];
            if(size[x] == 0)break;
            Edge *next = edge[x];
            for(;next;next = next->next)
            {
                if(flag[next->b])break;
            }
            cost[next->b] += tag[x]*next->t+cost[x];
            tag[next->b] += tag[x];
            lost[x] = (total - 2*tag[x])*next->t;
            flag[x] = false;
            p[x] = next->b;
            size[next->b] --;  //扩展完后非叶子又要变成叶子
            if(size[next->b] == 1)
                que[qn++]=next->b;
        }

//回溯

       if(x > 0){
            que[0] = x;
            qn = 1;
        }
        while(qn > 0)
        {
            x = que[--qn];
            flag[x] = true;
            Edge *next = edge[x];
            for(;next;next = next->next)
            {
                if(flag[next->b]||p[next->b] != x)continue;
                cost[next->b] = cost[x] + lost[next->b];
                que[qn++]=next->b;
            }
        }

//选择输出最优结果

        __int64 res = cost[1];
        int ls = 0;
        sov[ls++] = 1;
        for(i = 2;i <= n;i ++){
            if(cost[i] < res)
            {
                res = cost[i];
                ls = 1;
                sov[0] = i;
            }
            else if(cost[i] == res)
                sov[ls++] = i;
        }
        printf("%I64d\n%d",res*2,sov[0]);
        for(i = 1;i < ls;i ++)
            printf(" %d",sov[i]);
        printf("\n");

原创粉丝点击