题解 洛谷P5018【对称二叉树】(noip2018T4)

本文详细解析了NOIP2018 T4题目,通过使用深度优先搜索和双端队列实现对树结构的遍历,判断左右子树节点数量和权值是否相等,以寻找符合条件的子树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

\(noip2018\) \(T4\)题解

  • 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了
  • 毕竟我比较菜,不大会\(dp\)

  • 好了开始讲正事
  • 这题其实考察的其实就是选手对D(大)F(法)S(师)的掌握程度
  • 考完试有人说这题是马拉车,吓死我了
  • 首先,你把数据读入之后,先用一个大法师把以每个节点为根的子树的大小和权值都预处理出来,方便待会剪枝
  • 然后,你对以每个节点为根的子树,都判断一下以下条件(这时刚才处理的东西就有用了)

  • ① 左子树和右子树的节点数是否相等
  • ② 左子树和右子树的权值是否相等
  • ③ 以当前节点为根的子树大小是不是超过答案

  • 第三个很重要,不加(洛谷数据)最后一个点会TLE

  • 有一个显而易见的剪枝:因为答案至少是1,所以大小为1的子树就不用check了,不然浪费常数

  • 然后就是暴力判了
  • 递归下去,建立两个队列,保存当前处理到的左子树上和右子树上的节点,判左子树当前节点的左儿子和右子树当前节点的右儿子权值是否相等,右子树当前节点的左儿子和左子树当前节点的右儿子权值是否相等(注意对应)
  • 还有判下对应的节点有没有一个是空的一个没空的情况

  • 如果不相等就返回
  • 相等的话就扔进队列(注意对应顺序!)

  • 注意:上述处理一定要左右子树一起做,不能先处理一边,再处理另一边,不然会WA

  • 到最后如果都可以的话就return true

  • 附考场代码
  • 不得不说,为了能过,我加了一堆卡常
  • 3e6的输入规模应该还是要快读的吧

# include <bits/stdc++.h>

# define R register

const int MaxN = 1000010;

struct node//节点
{
    int val;
    int l, r;
};

node a[MaxN];
int f[MaxN], val[MaxN], ind[MaxN];//f[i]表示以i为根的子树大小,val表示以i为根的子树权值和,ind没啥用

inline void read(int &x)//快读
{
    x = 0;
    bool op = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
            op = 0;
        ch = getchar();
    }
    while(ch <= '9' && ch >= '0')
        x = (x << 1) + (x << 3) + (ch - '0'), ch = getchar();
    if(!op)
        x = -x;
}

void dfs(int root)
{
    if(root == -1)
        return;
    if(a[root].l == -1 && a[root].r == -1)
        f[root] = 1, val[root] = a[root].val;
    else
    {
        dfs(a[root].l);
        dfs(a[root].r);
        f[root] = f[a[root].l] + f[a[root].r] + 1;
        val[root] = val[a[root].l] + val[a[root].r] + a[root].val;
    }

}

inline int check(int x)
{
    std::queue<int> l, r;
    l.push(x), r.push(x);
    while(!l.empty() || !r.empty())
    {
        if(l.empty() || r.empty())
            return false;//一边空了,一边没空
        R int lx = l.front(), rx = r.front();
        l.pop(), r.pop();
        if(a[lx].val != a[rx].val)
            return false;
        R int lson[3], rson[3];
        lson[1] = a[lx].l, lson[2] = a[lx].r;//左子树当前节点的左儿子,左子树当前节点的右儿子
        rson[1] = a[rx].l, rson[2] = a[rx].r;//右子树当前节点的左儿子,右子树当前节点的右儿子
        if((lson[1] == -1 && rson[2] != -1) || (lson[1] != -1 && rson[2] == -1))
            return false;//一边空了,一边没空
        if((lson[2] == -1 && rson[1] != -1) || (lson[2] != -1 && rson[1] == -1))
            return false;//一边空了,一边没空
        if(lson[1] != -1)
            l.push(lson[1]);
        if(lson[2] != -1)
            l.push(lson[2]);
        if(rson[2] != -1)
            r.push(rson[2]);
        if(rson[1] != -1)
            r.push(rson[1]);
        //推进队列
    }
    return true;
}

int main()
{
//  freopen("tree.in", "r", stdin);
//  freopen("tree.out", "w", stdout);
    R int n;
    scanf("%d", &n);
    for(R unsigned i = 1; i <= n; ++i)
        read(a[i].val);
    for(R unsigned i = 1; i <= n; ++i)
        read(a[i].l), read(a[i].r), ++ind[a[i].l], ++ind[a[i].r];//处理入度
    R unsigned root;
    for(R unsigned i = 1; i <= n; ++i)
    {
        if(!ind[i])
        {
            root = i;
            break;
        }
    }//找树根
    dfs(root);//预处理
    int ans = 1;
    for(R unsigned i = 1; i <= n; ++i)//枚举子树
    {
        if(f[a[i].l] != f[a[i].r])
            continue;//剪枝1
        if(val[a[i].l] != val[a[i].r])
            continue;//剪枝2
        if(f[i] < ans || f[i] == 1)
            continue;//剪枝3
        if(check(i))
            ans = f[i];//更新答案
    }
    printf("%d", ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

转载于:https://www.cnblogs.com/little-sun0331/p/9965342.html

### NOIP 2018 复赛题目解析 #### 巴士调度问题分析 对于给定的巴士调度问题,存在 \( n \) 个人分别在不同的时间点 \( a_i \) 到达车站等待乘车。每辆大巴完成一次往返需要 \( m \) 分钟的时间。目标是最小化所有人的总等待时间。 为了实现这一目标,可以采用贪心策略来解决这个问题[^2]。具体来说: - 将所有的乘客按照到达时间升序排列。 - 对于每一个新的出发时刻,尽可能多地搭载当前已经在站点等候的人群直到满载或者达到下一个乘客到来之前。 - 计算每次发车时所有未被接走乘客额外增加的等待时间,并累加到总的等待时间之中去。 这种方法能有效地减少整体的等待时间,因为总是优先考虑那些已经等待较长时间的人先离开。 ```python def min_total_wait_time(n, m, arrival_times): # Sort the list of arrival times. sorted_arrivals = sorted(arrival_times) total_wait = 0 current_time = 0 while sorted_arrivals: next_departure = max(current_time, sorted_arrivals[0]) # Load passengers until bus leaves or no more people are waiting at this time point. while sorted_arrivals and sorted_arrivals[0] <= next_departure: del sorted_arrivals[0] # Update wait time for remaining passengers after departure. for t in sorted_arrivals: total_wait += (next_departure + m - t) current_time = next_departure + m return total_wait ``` 此代码实现了上述逻辑并计算出了最小化的总等待时间[^4]。 #### 关键挑战解决方案 当面对此类优化类问题时,核心在于如何合理规划资源分配以满足特定条件下的最优解。在这个案例里就是指怎样安排车辆运行计划让总体效率最高——即乘客们总的等待时间为最短。通过合理的数学建模以及有效的算法设计,可以很好地应对这类竞赛中的实际应用型考题[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值