P4395 [BOI2003]Gem 气垫车
P4395 [BOI2003]Gem 气垫车
题目大意:
一棵树,每个节点都有一个权值,相邻节点的权值不能相同,求一种方案使得整棵树的总权值和最小。$ n \leq 10000$.
思路:
有个结论是最大权值有个大致上界 \(\log_2(n) +1= 14\) 左右,考虑到上界很小,所以我们可以直接枚举上界。
我们设 \(f[i][j]\) 表示节点 \(i\) 权值为 \(j\) 时以 \(i\) 为根的子树的最小权值和。那么:
\[f[i][j] = \sum \min\{f[j][k]\}, i \to j, k \neq j
\]
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
int n, cnt, head[N], f[N][15];
struct Edge {
int to, next;
}e[N << 1];
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (!isdigit(c)) {
if (c == '-') f = -1;
c = getchar();
}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
return x * f;
}
inline void add(int x, int y) {
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
// f[i][j] 表示第i个节点权值为j时 它的子树的最小权值
// f[i][j] = sum_{ min_ f[son[i]][k], k != j}
inline void dfs(int x, int fa) {
for (int i = 1; i <= 15; ++i)
f[x][i] = i;
int to;
for (int i = head[x]; i ; i = e[i].next) {
to = e[i].to;
if (to == fa) continue;
dfs(to, x);
for (int j = 1; j <= 15; ++j) {
int Min = 0x7fffffff;
for (int k = 1; k <= 15; ++k) {
if (j == k) continue;
Min = min(Min, f[to][k]);
}
f[x][j] += Min;
}
}
}
int main() {
n = read();
int x, y;
for (int i = 1; i < n; ++i) {
x = read(), y = read();
add(x, y), add(y, x);
}
dfs(1, 0);
int Min = 0x7fffffff;
for (int i = 1; i <= 15; ++i)
Min = min(Min, f[1][i]);
printf("%d\n", Min);
return 0;
}