P5765 [CQOI2005] 珠宝

去洛谷看我的博客

思路

应该很容易想到使用树形 dp。

f u , i f_{u,i} fu,i 代表,只考虑 u u u 为根的子树, u u u 的编号为 i i i 的情况下,最小的编号总和。

那么我们可以用 u u u 的儿子 v v v 来更新 f u , i f_{u,i} fu,i

转移方程 f u , i = ∑ v ∈ s o n u min ⁡ f v , k [ i ≠ k ] + i f_{u,i}=\sum_{v\in son_u}\min f_{v,k}[i\neq k]+i fu,i=vsonuminfv,k[i=k]+i

那么可能染色的种数有多少种呢?

我们可以观察一下样例,发现,如果使用最小的 1 1 1 2 2 2 作为编号的话,至少也是 12 12 12,而正确答案呢,则是把一个有 3 3 3 个儿子的节点的编号定为 3 3 3,让儿子的编号从 2 2 2 变成 1 1 1

不妨假设某个节点,可以填入的最小编号是 1 1 1,那么如果他有 2 2 2 个儿子时,就应该填 2 2 2,让儿子更小,以获得更小的答案。

那么再继续假设,它的儿子里也有类似的情况,导致它的儿子必须填 2 2 2 才能让答案更小,所以这个节点必须填 3 3 3

那么,假设这个节点最后要填 x x x,就必然存在要填 x − 1 x-1 x1 x − 2 x-2 x2 x − 3 x-3 x3 等等的节点,可以发现如果 x x x 比较大,整个树的总结点数就非常大,如下图 x = 4 x=4 x=4 的情况:

P.S.蒟蒻不清楚是不是节点最少的情况,如有错误欢迎指出。

首先观察编号为 3 3 3 的节点,他不止需要一个编号必须为 2 2 2 的儿子,还需要另外的 3 3 3 个儿子,不然的话,可以让编号为 3 3 3 的节点变为编号 1 1 1,然后让儿子为 2 2 2 来获得更小答案,并且让它的父节点编号也可能变小。

所以编号为 4 4 4 的节点需要两个编号为 2 2 2 的节点和 5 5 5 个编号为 1 1 1 的节点。

可以发现总结点数增长很快,差不多是 2 n 2^n 2n 的样子,所以编号最大不超过 log ⁡ n \log n logn

差不多是 5 5 5 的样子,但是做的时候没细想,直接开了 20 20 20 绝对不会错。

AC code

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int n,e[100005],ne[100005],h[50005],idx=1,a,b,col[50005],dp[50005][20],ans=inf;
inline void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
void dfs(int u,int fa)
{
	for(int i=h[u];i;i=ne[i])
		if(e[i]!=fa)
		{
			dfs(e[i],u);
			for(int j=1;j<20;++j)
			{
				int res=inf;
				for(int k=1;k<20;++k) if(j!=k) res=min(res,dp[e[i]][k]);
				dp[u][j]+=res;
			}
		}
	for(int i=1;i<20;++i) dp[u][i]+=i;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;++i) scanf("%d%d",&a,&b),add(a,b),add(b,a);
	dfs(1,0);
	for(int i=1;i<20;++i) ans=min(ans,dp[1][i]);
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值