Miraclys

一言(ヒトコト)

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;
}
posted @ 2022-11-07 20:20  Miraclys  阅读(23)  评论(0编辑  收藏  举报

关于本博客样式

部分创意和图片借鉴了

BNDong

的博客,在此感谢