【题解】[CQOI2005] 珠宝
题目信息
题目来源:CCF 重庆省选 2005;
在线评测地址:Luogu#5765;
运行限制:时间 \(1.00\ \textrm{s}\),空间 \(128\ \textrm{MiB}\)。
(笔者没有在网上找到原题面,于是以下采用的是洛谷的精简题面)
题目描述
有一棵 \(n\) 个结点的树,给每个点安排一个正整数编号,使得相邻点具有不同的编号,编号的总和尽量小。
输入格式
第一行一个整数 \(n\)。
以下 \(n-1\) 行,每行两个数 \(u,v\),表示 \(u\) 和 \(v\) 间有一条边。
输出格式
仅一行,为最小编号和。
数据规模与约定
- 对于 \(20\%\) 的数据,\(n\le 10\);
- 对于 \(40\%\) 的数据,\(n\le 10^3\);
- 对于 \(100\%\) 的数据,\(1\le n\le 5\times 10^4\)。
保证 \(1\le u,v\le n\)。
分析
比较显然的树形 DP。(当时学树形 DP 的第一道例题就是这一个)
树上的问题通常要强行定根,然后进行 BFS 确定 DP 顺序,接下来进行 DP。这道题转移时要注意考虑这个节点的权值及其孩子的权值的可能性。
定义 \(f_{i,j}\) 为第 \(i\) 个点的权值为 \(j\) 时的最小值,则可以得到 \(f_{i,j}=v_{i,j} + \sum\limits_{i\rightarrow p} \min_{v=0}^{v\neq j} f_{p,v}\)。
接下来的问题是 \(j\) 的上界,这将会直接影响到复杂度。经试验开到 \(4\) 即可。
注意事项
注意 BFS 完后要倒序DP。
Code
#include <cstdio>
#include <cstring>
using namespace std;
constexpr int max_n = 50000, max_v = 4;
struct node // 队列节点
{
int id, fa;
node(int _i = 0, int _f = -1) : id(_i), fa(_f) { }
};
int dp[max_n][max_v], hd[max_n], des[max_n<<1], nxt[max_n<<1], e_cnt = 0;
node que[max_n];
void add_edge(int s, int t)
{
des[e_cnt] = t;
nxt[e_cnt] = hd[s];
hd[s] = e_cnt++;
}
int main()
{
memset(hd, -1, sizeof(hd));
int n, ta, tb, ql = 0, qr = 0;
node cur;
scanf("%d", &n);
for (int i = 1; i < n; i++) // 输入,建图
{
scanf("%d%d", &ta, &tb);
add_edge(ta - 1, tb - 1);
add_edge(tb - 1, ta - 1);
}
que[qr++] = node();
while (ql < qr) // BFS
{
cur = que[ql++];
for (int p = hd[cur.id]; p != -1; p = nxt[p])
if (des[p] != cur.fa)
que[qr++] = node(des[p], cur.id); // 防止死循环,加上判断
}
for (int i = n - 1; i >= 0; i--)
for (int j = 0; j < max_v; j++) // 第一重遍历
{
dp[que[i].id][j] = j;
for (int p = hd[que[i].id]; p != -1; p = nxt[p])
{
ta = max_v * max_n;
for (int k = 0; k < max_v; k++) // 第二重遍历
if (j != k)
{
if (dp[des[p]][k] < ta) // 更小就更新
ta = dp[des[p]][k];
}
dp[que[i].id][j] += ta;
}
}
ta = max_v * max_n;
for (int i = 0; i < max_v; i++) // 统计答案
if (dp[0][i] < ta)
ta = dp[0][i];
printf("%d\n", ta); // 输出
return 0; // 然后就 AC 了、
}
UPD on 2021-02-24,有关 \(j\) 的上限问题:
这道题,是此题很好的一个延伸。注意到此题讨论的就是 \(j\) 的上限 \(k\)。
不妨设根最大(为 \(k\)),则必须使子节点覆盖 \(1\) 到 \(k-1\)。同时,\(1\) 至少要有 \(k\) 个,否则就不能保证存在 \(k\) 最大值。
例外是 \(k-1\),可以只有一个,因为根为 \(k-1\) 会导致子树根为 \(k\),效果相同。
这样的话,定义 \(a_i\) 为最大值为 \(i\) 所需要的最小节点数,\(b_i=\sum\limits_{j=1}^{i-1} (i-j+1)b_j+1\),则:
注意到就是这个序列(我才不会告诉你我是看这个序列做出来的)。所以就可以 AC。
同时,回到原题,注意到 \(n\le 50000\),所以理论上 \(j\) 要取 \(11\) 才能保证。但这道题数据水啊,所以就这么过去了(迫真)。
本文来自博客园,作者 5ab,转载请注明链接哦 qwq
博客迁移啦,来看看新博客吧 -> https://5ab-juruo.oier.space/

浙公网安备 33010602011771号