一个FLAG #06# 二叉树两结点间的最短路径长度
题
二叉树两个结点之间的最短路径长度。完整题目见参考[1]。
#include <cstdio> #include <cstring> #define MAXN 20 int pre[MAXN]; // 存每个结点的父结点 int floor[MAXN]; // 存每个结点所在的层数,根结点在第1层 int d[MAXN][MAXN]; // 保存两个结点之间的最短路径长度 int getTheNearestCommonFather(int a, int b); // 找最近的公共祖先结点的id int getAns(int a, int b) { if (d[a][b]) return d[a][b]; // 若未计就计算,并保存计算过程中的结果 int f_id = getTheNearestCommonFather(a, b); if (!d[a][f_id]) { d[a][f_id] = d[f_id][a] = floor[a] - floor[f_id]; } if (!d[f_id][b]) { d[f_id][b] = d[b][f_id] = floor[b] - floor[f_id]; } d[a][b] = d[b][a] = d[a][f_id] + d[f_id][b]; return d[a][b]; } int main() { int T, n, m; // T数据组数 // n某组数据的结点数量 // m某组数据的查询次数 scanf("%d", &T); int left, right; // 临时存储某结点的左右子结点 int a, b; // 临时存储待查询的两个结点 while (T--) { scanf("%d %d", &n, &m); memset(pre, 0, sizeof(pre)); memset(floor, 0, sizeof(floor)); memset(d, 0, sizeof(d)); pre[1] = 1; // 根结点的祖先还是1 该初始化似乎多余 floor[1] = 1; // 根结点在第1层 for (int i = 1; i <= n ; ++i) { scanf("%d %d", &left, &right); if (left != -1) { pre[left] = i; floor[left] = floor[i] + 1; } if (right != -1) { pre[right] = i; floor[right] = floor[i] + 1; // 如果数据给出顺序不规则,这里的floor初始化会有逻辑错误,需要特别处理。 } } for (int i = 0; i != m; ++i) { scanf("%d %d", &a, &b); printf("ans: %d\n", getAns(a, b)); } } return 0; } // 找最近的公共祖先结点的id int getTheNearestCommonFather(int a, int b) { if (a == 1 || b == 1) return 1; int d; // 保存两结点的层数差 // 两结点不在同一层,转化为同一层 if (floor[a] > floor[b]) { d = floor[a] - floor[b]; while (d--) a = pre[a]; } if (floor[b] > floor[a]) { d = floor[b] - floor[a]; while (d--) b = pre[b]; } // 同一层的结点找最近的公共祖先结点id while (a != b) { a = pre[a]; b = pre[b]; } return a; }
例题6-6 小球下落 - 直接模拟最后一个小球下落
#include <cstdio> #include <cstring> int main() { int D, I; // D叶子深度 I小球个数 int k; while (scanf("%d%d", &D, &I) == 2) { k = 1; // 依然用 k 来标识小球的当前位置 for (int i = 1; i < D; ++i) { if (I % 2 != 0) { // 奇数球往左走 k = k * 2; I = (I + 1) / 2; // 相对左树,是第几个球 } else { // 偶数球往右走 k = k * 2 + 1; I = I / 2; } } printf("%d\n", k); } return 0; }