题意理解

一叶知秋。如果要平衡,那么,末端的二叉左右两片叶子一定是相等的。好吧,延伸开来,所有的左右子树都要是相等的。所以,由一片叶子就可以推知整棵树的情形。

Untitled

如果使用3作为参考砝码,那么整个天平的重量是 3×2^2=12,需要修改砝码7为3。

如果使用7作为参考砝码,那么整个天平的重量是7×2^2=28,需要修改砝码3为7,6为14。

如果使用6作为参考砝码,那么整个天平的重量是6×2^1=12,需要修改砝码7为3。

我们发现三次参考砝码的选择中,有两次得到的天平总重量都是12,也就是说,这两个砝码如果修改其中一个,另一个就不用改。那么我们把使用每一个砝码得到的天平总重记录起来,找到其中出现次数最多的那个,然后我们用砝码数减去它就是最少修改砝码数了。

那么,这里背后的数学原理是什么呢?

稍微想一下其实是比较容易证明的,或者说容易厘清的。为什么呢?因为只要最后得到的天平的重量是一样的,那么所有会导致这种重量的方案的砝码其实是都不用动的。减掉这些不用动的,自然就是需要修改的砝码的数量。

代码理解

#include <cassert>
#include <cmath>
#include <string>
#include <iostream>
#include <functional>
#include <algorithm>
#include <map>

using namespace std;
const int MAXN = 24;
typedef long long LL;

int main()
{
    int T;
    cin >> T; // 读入测试用例数量 T

    string line;
    map<LL, int> vCnt; // 使用map来记录每个节点值的出现次数

    while (T--)
    {
        cin >> line; // 读入一行表示平衡二叉树的表达式
        vCnt.clear(); // 清空上一个测试用例的节点值统计

        int sz = line.size(), depth = 0, nodeCnt = 0; // sz表示字符串长度,depth表示当前节点深度,nodeCnt表示节点总数

        for (int i = 0; i < sz; i++)
        {
            char c = line[i];

            if (c == '[')
                depth++; // 遇到'[',深度加一
            else if (c == ']')
                depth--; // 遇到']',深度减一
            else if (isdigit(c))
            {
                // 遇到数字,解析节点值
                LL v = c - '0';
                int j;
                for (j = i + 1; j < sz && isdigit(line[j]); j++)
                {
                    v *= 10;
                    v += line[j] - '0';
                }
                i = j - 1;
                v <<= depth; // 节点值左移depth位,考虑到当前节点的深度,也就是上面所说的,使用 v 这个值作为参考砝码的情况
                vCnt[v] = vCnt[v] + 1; // 更新节点值的出现次数
                nodeCnt++; // 节点总数加一
            }
        }

        int K = -1;
        for (const auto &p : vCnt)
            K = max(K, p.second); // 找到节点值出现次数的最大值

        assert(K > 0); // 断言确保K大于0
        cout << (nodeCnt - K) << endl; // 输出结果,节点总数减去出现次数最多的节点值的次数
    }
    return 0;
}

参考:https://www.cnblogs.com/lilpig/p/14006514.html