一开始想得太简单, 直接读数据建立树, 然后像堆调整那样一个个结点检查并向上调整子节点的重量, 后来写到一半发现这样不可行, 需要处理的情况太多, 这样模拟的过程太繁琐了..
于是可耻地google了答案——原来这棵树每层的结点值都是满足一定规律的,在同一层上的结点值必须相等, 而且上层的值是下一层的节点值的两倍。
认识到这个规律后,还是需要做一点巧妙的处理(方法来自这里 ) :
由于只要选择一个叶节点作为标准就可以确定一棵树的总重量,
因此遍历每个叶节点, 确定以其作为标准时树的总重量,
那么最多叶节点对应的那个总重量就是树应该取的最终总重量 M,
用总的叶结点数减去M所对应的叶节点数即可得到需要改变的叶节点数.
还有一个需要注意的地方就是由于结点的值正好压了int的范围, 所以树的总重量肯定有可能超过int, 因此要用 long long 来存储树的总重量
以下为模仿答案写的代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <list>
#include <cassert>
#include <iomanip>
#pragma warning(disable:4996) //关掉4996警告
/*
Uva 439
关键 : 1. 事先找出规律!!!
2. 技巧 : 由于只要选择一个叶节点作为标准就可以确定一棵树的总重量,
因此遍历每个叶节点, 确定以其作为标准时树的总重量, 那么
最多叶节点对应的那个总重量就是树应该取的最终总重量 M,
用总的叶结点数减去M所对应的叶节点数即可得到需要改变的叶节点数
*/
using namespace std;
int Leaves;
string input;
int ptr;
map<long long, int> WeightNum; // 记录树的某个总重量所对应的标准结点数
void Build(int Depth) {
char ch;
ch = input[ptr++];
if ( ch == '[' ) {
Build(Depth+1);
ch = input[ptr++]; // 读 ','
Build(Depth+1);
ch = input[ptr++]; // 读 ']'
}else { // 找到一个叶节点
long long sum = 0;
do {
sum = sum * 10 + ch - '0';
ch = input[ptr++];
} while ( ptr < input.size() && ch != ',' && ch != ']' );
ptr--;
WeightNum[sum << Depth]++;
Leaves++;
}
return;
}
int main( ) {
//freopen("input.txt", "r", stdin);
int N;
cin >> N;
for ( int i = 0; i < N; i++ ) {
WeightNum.clear( );
cin >> input;
Leaves = ptr = 0;
Build(0);
int mostNum = 0;
for ( auto it = WeightNum.begin( ); it != WeightNum.end(); it++ ) {
mostNum = max(mostNum, it->second);
}
cout << Leaves - mostNum << endl;
}
//system("pause");
return 0;
}