#2104 叶子的颜色 【树形dp】

3 篇文章 1 订阅

(题面可自行跳过)


描述

给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。

对于每个叶结点u,定义c[u]为从根结点到u的简单路径上最后一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。

输入
第一行包含两个正整数m, n,其中n是叶子的个数,m是结点总数。结点编号为1,2,…,m,其中编号1,2,… ,n是叶子。以下n行每行一个0或1的整数(0表示黑色,1表示白色),依次为c[1],c[2],…,c[n]。以下m-1行每行两个整数a,b(1<=a < b <= m),表示结点a和b 有边相连。

输出
仅一个数,即着色结点数的最小值

样例输入 [复制]
5 3
0
1
0
1 4
2 5
4 5
3 5
样例输出 [复制]
2

数据规模:
m<=10000 n<=5000


思路:
本身不难,但是个人认为比较经典


状态转移不一定 要完全保留 子状态 ,可以进行 改变,满足最优性
只需要 子状态是平行比较中最优的


首先容易想到贪心

对于一个有根树,
显然尽量向上染色能影响的节点最多。。 仅有当前节点的儿子中 不同颜色都有,就把数量少的颜色一个个染, 数量多得作为一个传上去。
但是,这样无法处理两种颜色数量相同的情况。。

所以转向dp

f[i][k]+= min(f[son][k]-1,f[son][k^1])

k代表颜色 (0或1) , i是当前子树的根节点

思考状态定义:显然为 以i为根节点的子树最少次数。
此题的最优子结构的染色方法肯定要改变,
所以 思考如何当前步操作,使最优子结构可以更新出当前子结构。

结合贪心,我们还是把染色点尽量上放,那么在确定当前节点染色 (0或1),可以把染相同颜色的子节点都统一染在 当前点,总数依次-1
不同的就保留
最后所得值+1(当前要染色)

第二步:
思考换根, 对于当前根的儿子,如果有不同,仅限于这两个结点都染色的情况。
根据贪心策略(或者状态转移)
显然相邻两个节点颜色不相同(否则染在根上即可),那么其中任意一个作为根,
答案相同。

所以不需要换根。。。

//代码中并没有用数组,直接用了pair。。还要慢一点。。。


代码:

#include<bits/stdc++.h>
#define pf printf
#define sf scanf
#define fi first
#define se second
using namespace std;
const int maxn=1e4+10;
typedef pair<int,int> T;
vector <int> G[maxn];
int col[maxn];
//f[i][k]+=  min(f[son][k]-1,f[son][k^1])
inline T dfs(int u,int fa){
	T t,ans;
	if(col[u]!=-1){
		return col[u]==0? make_pair(1,0x5f5f5f5f) : make_pair(0x5f5f5f5f,1);
	}
	for(int i=0;i<G[u].size();++i){
		int v=G[u][i];if(v==fa)continue;
		t=dfs(v,u);
		ans.fi+=min(t.fi-1,t.se);
		ans.se+=min(t.fi,t.se-1);
	}
	ans.fi+=1;ans.se+=1;
	return ans;
}

signed main (){
	memset(col,-1,sizeof col);
	int n,m;
	sf("%d%d",&m,&n);
	for(int i=1;i<=n;++i){
		sf("%d",&col[i]);
	}
	for(int i=1;i<m;++i){
		int a,b;sf("%d%d",&a,&b);
		G[a].push_back(b);G[b].push_back(a);
	}
	T ret=dfs(n+1,-1);
	pf("%d",min(ret.fi,ret.se));
	   return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值