luogu 3155 [CQOI2009]叶子的染色
题目描述
给一棵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<=5021
分析
只有两种颜色,会好办得多
每一个节点,有三种可能:
不涂颜色/白色/黑色
而儿子如果有颜色,就可以屏蔽父亲的颜色
如果根是确定的,这道题就是一个树形DP
首先,明白一个道理
对于每一个子树,根涂色,比根不涂色方案一定会更优
因为明显根的影响比任何一个子孙都大,儿子能做的,根都能做,儿子不能做的,根也能做
所以我们可以不用记录不涂色这一状态
dp[节点][0/1]
如果节点v不染色,就是dp[v][0/1] - 1
如果根的颜色与儿子一样,那么儿子就不用涂色,如果不一样,儿子就涂色
转移方程:
关于初值:对于不是叶节点的点,初值为1,
而叶节点,如果与他所需的颜色符合,便赋为1,不同则赋为2(或极大值)
看转移方程,如果叶节点染的颜色与自身所需不同,那么当父节点的颜色也与自身所需不同,这个节点就必须染回来,那么他对答案的贡献为
dp[v][与所需不同] - 1 = 1;
如果当父节点的颜色也与自身所需相同,那么它不染色最好,赋为2的那一个状态对答案无影响
其实我觉得赋成极大值表示这种情况不能选也是可以的
好了,这是根固定的情况
如果根不固定?
换根,其实就是将旧根到新根的路径上的节点的父子关系颠倒一下,其余节点都不变
颠倒父子关系对答案有什么影响吗?
没有影响
因为儿子如果有颜色,就可以屏蔽父亲的颜色
就算从方程来看,也就是将生成答案的几项调换了一下顺序,答案是不变的
代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************* 2 User:Mandy.H.Y 3 Language:c++ 4 Problem:luogu3155 5 Algorithm: 6 *********************/ 7 #include<bits/stdc++.h> 8 9 using namespace std; 10 11 const int maxn = 5050; 12 const int maxm = 10005; 13 14 int n,m,size; 15 int first[maxm]; 16 bool c[maxn]; 17 int dp[maxm][5]; 18 19 struct Edge{ 20 int v,nt; 21 }edge[maxm<<1]; 22 23 template<class T>inline void read(T &x){ 24 x = 0;bool flag = 0;char ch = getchar(); 25 while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); 26 while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); 27 if(flag) x = -x; 28 } 29 30 template<class T>void putch(const T x){ 31 if(x > 9) putch(x / 10); 32 putchar(x % 10 | 48); 33 } 34 35 template<class T>void put(const T x){ 36 if(x < 0) putchar('-'),putch(-x); 37 else putch(x); 38 } 39 40 void file(){ 41 freopen("3155.in","r",stdin); 42 // freopen("3155.out","w",stdout); 43 } 44 45 void eadd(int u,int v){ 46 edge[++size].v = v; 47 edge[size].nt = first[u]; 48 first[u] = size; 49 } 50 51 void readdata(){ 52 read(m);read(n); 53 for(int i = 1;i <= n; ++ i) read(c[i]); 54 for(int i = 1; i<m; ++ i){ 55 int u,v; 56 read(u);read(v); 57 eadd(u,v);eadd(v,u); 58 } 59 } 60 61 void dfs(int u,int fa){ 62 if(u > n) dp[u][1] = dp[u][0] = 1; 63 else dp[u][c[u]] = 1,dp[u][c[u]^1] = 2; 64 for(int i = first[u];i;i = edge[i].nt){ 65 int v = edge[i].v; 66 if(v == fa) continue; 67 dfs(v,u); 68 dp[u][0] += min(dp[v][0]-1,dp[v][1]); 69 dp[u][1] += min(dp[v][1]-1,dp[v][0]); 70 } 71 } 72 73 void work(){ 74 dfs(n+1,0); 75 put(min(dp[n+1][0],dp[n+1][1])); 76 } 77 78 int main(){ 79 // file(); 80 readdata(); 81 work(); 82 return 0; 83 }