• NWERC 2013


    题目描述:给你一颗二叉树,保证每个点要么是叶子节点,要么有左右两个儿子。某些叶子节点上放着灯,请你移动最少的灯,使得整棵树平衡

    对平衡的定义:对于树上的每个点左右儿子中灯数的差的绝对值≤1,那么这棵树平衡。总灯数sum≤1000.

    解题思路:题目中强调的是移动,不能往里面添加,也不能从树中拿走。这个移动操作就给我们造成了一些困难,起初想一些dp的方法,感觉都不是很靠谱。

    于是看了官方题解,题解中用了一步巧妙地转化,然后递归求解,很有借鉴意义。

    首先对于移动操作次数最少,我们可以转化成,往树里插入sum个点,尽量把点放入本来有灯的位置,最大覆盖多少个的问题。那么原来没有灯,现在要放点,这些位置的个数就是答案。

    完成这一步转化之后就要挖掘题目性质,绝对值差1这个性质十分重要,因为这近似于一步二分,也就是如果我们从顶点开始递归地往下放,那么最多进行logSUM层就能使得剩下的 可放点数达到0或1,这就使我们得到一种很可行的分治求解的方法:Get(x, cnt)表示向x这个点插入cnt个灯,向空点放的情况最少有多少个(也就是移动次数最少有多少次)。对于cnt是偶数的情况,就直接Get(x.left, cnt >> 1), Get(x.right, cnt >> 1), 而对于cnt是奇数,就要讨论一下左右放多放少这两种情况,取个min了。

    递归的终止条件:①cnt == 0,返回0 ②x == 0 && cnt > 0返回INF(无解)③cnt == 1 子树中本来有灯返回1,无灯返回0

    P.S.题目中并没有给总点数,经过分析总点数不会超过叶子节点的二倍。但是题目读入比较繁琐,字符串长度要开至少4000。

     1 #include <cstdio>
     2 #include <cstdlib>
     3 #include <cstring>
     4 #include <cmath>
     5 #include <algorithm>
     6 using namespace std;
     7 
     8 const int MaxN = 3000;
     9 const int INF = 2000;
    10 struct Node {
    11     int left, right, father;
    12     int cLoc, cOn;
    13 }tree[MaxN + 5];
    14 int n, tot, ans;
    15 char s[MaxN * 2 + 5];
    16 
    17 void Init()
    18 {
    19     memset(tree, 0, sizeof(tree));
    20     int len = strlen(s);
    21     int p = 0; n = 0;
    22     for (int i = 0; i < len; i++) {
    23         if (s[i] == '(') {
    24             if (tree[p].left == 0) tree[p].left = ++n;
    25                 else tree[p].right = ++n;
    26             tree[n].father = p;
    27             p = n;
    28         }
    29         else if (s[i] == 'B') {
    30             tree[p].cOn = 1;
    31             tree[p].cLoc = 1;
    32             p = tree[p].father;
    33             i++;
    34         }
    35         else {
    36             int L = tree[p].left;
    37             int R = tree[p].right;
    38             tree[p].cOn = tree[L].cOn + tree[R].cOn;
    39             tree[p].cLoc = tree[L].cLoc + tree[R].cLoc;
    40             if (tree[p].cLoc == 0) tree[p].cLoc = 1;
    41             p = tree[p].father;
    42         }
    43     }
    44     //for (int i = 1; i <= n; i++) printf("%d %d %d
    ", i, tree[i].cOn, tree[i].cLoc);
    45 }
    46 
    47 int Get(int x, int cnt)
    48 {
    49     if (cnt == 0) return 0;
    50     if (x == 0) return INF;
    51     if (cnt == 1) return (int)(tree[x].cOn == 0);
    52     if (cnt & 1) {
    53         int cL1 = Get(tree[x].left, cnt >> 1);
    54         int cR1 = Get(tree[x].right, cnt - (cnt >> 1));
    55         int cL2 = Get(tree[x].left, cnt - (cnt >> 1));
    56         int cR2 = Get(tree[x].right, cnt >> 1);
    57         return min(cL1 + cR1, cL2 + cR2);
    58     }
    59     else {
    60         int cL = Get(tree[x].left, cnt >> 1);
    61         int cR = Get(tree[x].right, cnt >> 1);
    62         return cL + cR;
    63     }
    64 }
    65 
    66 int main()
    67 {
    68     while (~scanf("%s", s)) {
    69         Init();
    70         ans = Get(1, tree[1].cOn);
    71         if (ans >= INF) printf("impossible
    ");
    72             else printf("%d
    ", ans);
    73     }
    74 }
    View Code
  • 相关阅读:
    ORA-01940: cannot drop a user that is currently connected
    三分钟入门VyOS网络操作系统
    金笛短信猫发短信一段时间后,停止发送
    ORA-01940 无法删除当前已连接的用户之解决方案
    Table is marked as crashed and should be repaire (
    DirectConnect API
    使用 ElasticSearch Aggregations 进行统计分析
    Cocos2d-x学习笔记(四) 布景层的加入移除
    Spark源代码阅读笔记之DiskStore
    一个搜索迷宫出路的程序
  • 原文地址:https://www.cnblogs.com/ChopsticksAN/p/5852272.html
Copyright © 2020-2023  润新知