题目描述
给一棵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
这题是树形DP,DP先建模:
设Dp[i][flag]
为当前以i为根子树内还需求的颜色只剩flag的最小放置数(不包含i点) //如果剩两种颜色,就不能通过在i点涂色来达到目的
那么DP[i][flag]=sum(min(DP[i.son][flag],DP[i.son][!flag]+1))//i.son指i的儿子,如果i.son涂另一种颜色更优,则需要在i.son点涂色一次才能维护。
这就是题解。(预处理特别恶心,具体看代码)
AC代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#define maxn 10005
#define num ch-'0'
using namespace std;
int n,m,c[maxn],u,v,cnt,fir[maxn],dp[maxn][2];
struct node{
int nxt,to;
}e[maxn*2];
void Make(int s,int t){
e[++cnt].to=t;
e[cnt].nxt=fir[s];
fir[s]=cnt;
}
void get(int &res){
res=0;
char ch;
while(!isdigit(ch=getchar()));
for(res=num;isdigit(ch=getchar());res=res*10+num);
}
void dfs(int now,int past){
int z[2]={};
for(int i=fir[now];i;i=e[i].nxt){
if(e[i].to==past) continue;
dfs(e[i].to,now);
for(int j=0;j<2;j++){
if(dp[e[i].to][j]!=dp[maxn-1][0]&&dp[now][j]==dp[maxn-1][0]) dp[now][j]=0;
if(dp[e[i].to][!j]<dp[e[i].to][j]) z[j]++;
dp[now][j]+=min(dp[e[i].to][j],dp[e[i].to][!j]);
}
}
for(int i=0;i<2;i++) dp[now][i]+=z[i];
}
int main(){
memset(dp,127/12,sizeof dp);
get(m),get(n);
for(int i=1;i<=n;i++){
get(c[i]);
dp[i][c[i]]=0;
}
for(int i=1;i<m;i++){
get(u),get(v);
Make(u,v);
Make(v,u);
}
dfs(m,m);
printf("%d",min(dp[m][0],dp[m][1])+1);
}