【xdoj】1164: 男神的树 以及一些关于内存的问题

这道题不算太难,不过的确给我造成了很大的麻烦。一开始看到的时候,觉得只要一个dfs就可以轻松做出来了,结果没想到真的去尝试的时候才发现一个意外的问题——内存超限,说实话时间超限我已经经历得很多了,但内存超限真的还是没怎么遇到过,所以发现有点手足无措,经过了一些尝试依然没有办法有效解决这个问题,于是我就开始了一些测试。

首先看看内存的排布,我们的机子首先要存储最大范围内的元素,因为这道题的n高达1e7,所以很轻松就可能会超限,我先科普一下,这里的内存用字节进行统计即可,一个int是4字节,longlong是8字节,所以存储一个int类型的大小就是4e7字节,而这道题的内存限制是常规的128m,也就是大约1.28e8字节,相当于这个大小的三分之一,也就是说存三个int类型的就超限了,另外我也测试了一下vector,1e6的时候就已经达到了24644,这个根本不能存在,否则直接超限,另外关于stack其实挺神奇的,记得这个东西其实并不是容器本身,而是一种借助其它容器实现的规则,所以stack的内存和同类型的数组是差不多的,另外longlong和pair【int , int】(这里用大于等于号不知道为什么好像会导致之后的字被吃掉) 基本都是int的两倍。

先说说常规想法,就是做一个dfs,因为开头无法被影响所以先搞开头,然后再一点一点往下递归。

经过了这些研究,我们至少得到了一个结论,那就是vector用不了了,所以必须用更小的办法存储路径,想来想去好像只有int数组比较靠谱,之前本来想用邻接表的,但是那样还要有一个head,加上本身的值就应经是三个int数组了,那这样就没有空间来进行递归了,而递归的时候要存储一个ll类型的和一个int类型的,这又是一笔巨大的开支,所以我觉得可能这道题并不能用递归来解决。那这样我能想到的就只能有用数组存贮一个父亲节点par,这样的确最省内存,但是因为方向的问题就不能进行递归了,又想了一阵之后发现其实大可不必递归,因为可以意识到一个非常有趣的地方,就是一个节点的改变有且仅有改变他和父亲节点的差距(不考虑这个父亲其他儿子的情况),因为儿子的儿子管不到他们了,而父亲的父亲又是同时管他们,所以父亲和儿子的差距只能由儿子来解决,这样其实每个儿子到底要消除多少其实之和本身和父亲的值有关,而原始节点只要消除他的绝对值即可,这样问题就迎刃而解了。

贴代码

# include <stdio.h>
# include <string.h>

typedef long long ll;

const int MAX_N = 1e7 + 1;

int par[MAX_N];
int A[MAX_N];
int N;

int main()
{
    while(~scanf("%d", &N))
    {
        memset(par , 0 , sizeof(int) * (N + 1));

        int i, u, v;
        for(i = 0 ; i < N - 1 ; i++)
        {
            scanf("%d %d", &u, &v); 
            par[v] = u;
        }

        for(i = 1 ; i <= N ; i++)
            scanf("%d", &A[i]);

        ll ans = 0;
        for(i = 1 ; i <= N ; i++)
        {
            ans += (A[i] - A[par[i]]) < 0 ? -(A[i] - A[par[i]]) : (A[i] - A[par[i]]); 
        }

        printf("%lld\n", ans);
    }

    return 0;
} 
阅读更多
换一批

没有更多推荐了,返回首页