0x01 前置芝士
树形结构?贪心?思维?眼睛?
好有趣。。。 link
0x02
题目大意:给你一颗有 (n) 个节点的树,你需要尽可能多的删掉边,使得剩下的图中有 (k) 个点满足互相能走到。求最后剩下的边数。
我们深度剖析出题人其实是想考树形 (dp) 的,可是呢,这其实就是道一眼题。
首先,我们剩下的图最优情况一定是只剩 (k) 个点。
对于答案,最优的情况其实就是 (k) 个点分成 (lfloor frac k 2 floor) 堆,使得每一堆只有两个点或有一堆有三个点。这也是最理想的情况,但显然一些树无法满足。不过你会发现,这个理想情况最后剩的边为 (lceil frac k 2 ceil)。
于是我们考虑原树可以拆分成多少个多少堆,使得每一堆的点数不大于 (2),记能拆分出的个数为 (cnt)。
如果 (cnt >= lfloor frac k 2 floor),也就是说这棵树可以拆分出这么多堆,那么答案就是 (lceil frac k 2 ceil)。
而如果 (cnt < lfloor frac k 2 floor),也就是说这棵树是不支持最优解的,那么我们就运用一下贪心。首先我们需要剩余 (k) 个点,而因为我们原树只能拆出 (cnt) 堆,所以说这 (k) 个点中,一定有 (cnt imes 2) 个点可以成为理想情况。那么我们就考虑剩下 (k - cnt imes 2) 个点怎么办。其实我也不知道怎么办,但我知道这些点一定能通过一条边和已经处于理想情况的一堆连接起来,那么累加边即可。此时答案为 (k - cnt imes 2 + cnt),即 (k - cnt)。
把这两种情况综合一下就会得到答案:(k - min(lfloor frac k 2 floor, cnt))。
关于 (cnt) 的求法,我们可以直接遍历这棵树。
对于节点 (u),它的子节点 (v),如果所有的 (v) 被取用过,我们就不取,并把 (u) 设为未被取用;如果有一个 (v) 没有被取用,我们就取没被取用的第一个 (v) 并把 (u) 设为被取用。可以结合代码分析。
0x03 code
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
int read() {
int x = 0, k = 1;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * k;
}
void write(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
void print(int x, char s) {
write(x);
putchar(s);
}
int Min(int x, int y) { return x < y ? x : y; }
const int MAXN = 1e5 + 5;
int son[MAXN], dp[MAXN];
vector<int> mp[MAXN];
void Add_Edge(int u, int v) {
mp[u].push_back(v);
mp[v].push_back(u);
}
int cnt = 0;
bool Tree_Dp(int u, int fa) {
bool f = 1;
for (int i = 0; i < mp[u].size(); i++) {
int v = mp[u][i];
if (v == fa)
continue;
int t = Tree_Dp(v, u);
if (f && t) {
f = 0;
cnt++;
}
}
return f;
}
int main() {
int T = read();
while (T--) {
int n = read(), k = read();
for (int i = 1; i <= n; i++) mp[i].clear();
for (int i = 1; i < n; i++) {
int u = read(), v = i + 1;
Add_Edge(u, v);
}
for (int i = 1; i <= n; i++) son[i] = mp[i].size();
cnt = 0;
Tree_Dp(1, 0);
print(k - Min(cnt, k >> 1), '
');
}
return 0;
}