P11206 「Cfz Round 9」Dove 题解
normalpcer · · 题解
题意简述
给定一棵树,希望给其上的所有点编号,使得所有边的两端端点编号之和均不超过
分析
可以使用如下的贪心策略进行编号:
- 按照从深到浅的顺序排序所有点。
- 按照顺序,将一个点编上尽可能大的号码,同时,如果它的父节点没有被编号,则将其父节点编上尽可能小的号码。
接下来,我们对这个策略的正确性进行一个简要的证明。
使用数学归纳法。我们称在一个拥有
对于
假设
我们设要编号为
如图,我们把
接下来,考虑这个过程中去掉的边(
特别地,考虑
故由数学归纳法,原命题成立。操作
代码
这里不多加阐述,可以参考下方的代码和注释。
/**
* @link https://www.luogu.com.cn/problem/P11206
*/
#include <bits/stdc++.h>
#define upto(i,n) for(int i=1;i<=(n);i++)
int T;
namespace Solution {
int N;
const int _N = 1e5+5;
int depth[_N]; // 到根节点(1)的距离
std::vector<int> conn[_N]; // i 和 conn[i][j] 互相连接
int parent[_N]; // 记录父亲节点
int filling_queue_arr[_N];
int filled[_N];
std::queue<int> filling_queue;
void init() {
// 重设,防止多测数据互相污染
std::memset(depth, 0, sizeof(depth));
upto(i, _N) conn[i].clear();
std::memset(parent, 0, sizeof(parent));
std::memset(filling_queue_arr, 0, sizeof(filling_queue_arr));
std::memset(filled, 0, sizeof(filled));
scanf("%d", &N);
int x=0, y=0;
upto(_, N-1) {
scanf("%d%d", &x, &y);
conn[x].push_back(y);
conn[y].push_back(x);
}
}
void dfs_depth(int p, int prev) { // 计算深度
parent[p] = prev;
depth[p] = depth[prev] + 1;
for (auto &dest: conn[p]) {
if (dest == prev) continue;
dfs_depth(dest, p);
}
}
bool check() {
upto(i, N) {
for (auto &dest: conn[i]) {
if (filled[i] + filled[dest] > N+1) {
return false;
}
}
}
return true;
}
void solve() {
init();
dfs_depth(1, 0); // 以 1 为根节点
// 按照深度排序填充顺序
upto(i, N) filling_queue_arr[i] = i;
std::sort(filling_queue_arr+1, filling_queue_arr+1+N, [](auto a, auto b) {
return depth[a] > depth[b];
});
upto(i, N) filling_queue.push(filling_queue_arr[i]); // 压入队列
int L=1, R=N; // 区间最小值和最大值
// 依次填充
while (not filling_queue.empty()) {
auto cur = filling_queue.front(); filling_queue.pop();
if (filled[cur]) continue; // 填过的就忽略
// 尝试在该点填充一个最大的数
filled[cur] = R; R--;
// 在父节点填充一个最小的数
if (parent[cur] != 0 /* 特判,根节点不做处理 */ && not filled[ parent[cur] ]) {
filled[ parent[cur] ] = L; L++;
}
}
// 全部填充完毕
upto(i, N) printf("%d ", filled[i]);
assert(check()); // 结果正确
printf("\n");
}
}
int main() {
scanf("%d", &T);
while (T --> 0)
Solution::solve();
return 0;
}
这里的 check() 函数事实上是无用的,不过可以方便调试。
此外,还是要提醒一下,对于多测的题目,要注意完全清空所有可能用到的数组和容器。