Description
给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。
对于每个叶结点u,定义c[u]为从u到根结点的简单路径上第一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。
Input
第一行包含两个正整数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 有边相连。
Output
仅一个数,即着色结点数的最小值。
Sample Input
5 3
0
1
0
1 4
2 5
4 5
3 5
Sample Output
2
Hint
【数据范围】
1<=N,M<=100000
思路
通过感性推理,多次试验证明选哪个点作为根节点都是一样的!!!
我们考虑树形DP
设f[i][0..1]为第i个节点的子树,i染0..1颜色的最小染色次数。
由于我们设定i一定要染色,所以初始值是1。
假设i染黑色,如果它的子节点也染黑色,可以发现是不用染的,所以染色点数减一
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=100077;
vector<int> a[maxn];
int n,m,b[maxn],ass=0,ans=0,f[maxn][2],c[maxn];
void dfs(int u,int fa)
{
f[u][0]=1; f[u][1]=1;
if(u<=n) f[u][!c[u]]=0x3f3f3f3f;
for(int i=0; i<b[u]; i++) if(a[u][i]!=fa)
{
int x=a[u][i];
dfs(x,u);
f[u][0]+=min(f[x][0]-1,f[x][1]);
f[u][1]+=min(f[x][1]-1,f[x][0]);
}
}
int main()
{
scanf("%d%d",&m,&n);
// memset(f,sizeof(f),0x3f);
for(int i=1; i<=n; i++)
{
int x;
scanf("%d",&c[i]);
}
for(int i=1; i<=m-1; i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[x].push_back(y); a[y].push_back(x);
b[x]++; b[y]++;
}
// memset(d,0,sizeof(d));
dfs(n+1,0);
printf("%d",min(f[n+1][0],f[n+1][1]));
}