[CQOI2005]珠宝
题目大意
给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数。
唯一的限制条件是相临的两个结点不能标上相同的权值,要求一种方案,使得整棵树的总价值最小。
解题思路
本题相当于一个染色问题。
本蒟蒻开始以为是 染色。(危
其实可以有一组 Hack
数据。
将数据构造成一棵树:
如果是 染色,则总价值最小的染色情况为:
如图可知,总价值为 ,与输出不符,故不能用 染色。
再根据图进行分析,可以发现,对总价值贡献最多的是节点 的 个子节点,贡献为 。
那能不能修改通过修改图的一个节点,使得这 个节点的贡献减少呢?
答案是,可以。
怎么做?
将节点 的权值修改为 就行了,若如此做,其 个子节点的权值都可修改为 ,故权值之和为 。
则正确的染色方法为:
再来一组数据。 (还有完没完
根据两种情况:
- 一个节点的权值只受到他的父节点和子节点的影响。
- 这是一棵树,一定存在 dfs 序。
可以想到这道题正解其实是 树形dp
。
定义 为 节点 的权值为 时以 为根节点的子树的点权总和的最小值。
现在要找一个最大可以填的数,为 。
我们可以出于最坏的情况来进行考虑,当周围的点呈现出 这样的倍增的情况的时候,我们可以发现,中间的点按照升序排列,而周围的点变成都是 的会更好一些,所以,最大可以填的数为 。
为了不 WA
, 数组的第二维应取到 。
所以转移方程为:
AC CODE
#include <bits/stdc++.h>
using namespace std;
const int _ = 2e5 + 5;
int n, f[_][5], ans = INT_MAX;
int tot, head[_], to[_], nxt[_];
void add(int a, int b)
{
nxt[++tot] = head[a];
head[a] = tot;
to[tot] = b;
}
void dfs(int x, int fa)
{
for (int i = 1; i <= 4; i++)
f[x][i] = i;
for (int i = head[x]; i; i = nxt[i])
{
int v = to[i];
if (v == fa)
continue;
dfs(v, x);
for (int j = 1; j <= 4; j++)
{
int minn = INT_MAX;
for (int k = 1; k <= 4; k++)
if (j != k)
minn = min(minn, f[v][k]);
f[x][j] += minn;
}
}
}
signed main()
{
scanf("%d", &n);
for (int i = 1, a, b; i < n; ++i)
{
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
}
dfs(1, 0);
for (int i = 1; i <= 4; i++)
ans = min(ans, f[1][i]);
printf("%d", ans);
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122169
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话