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
M<=10000
N<=5021
题解
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
const int N=12000,M=25000;
const int INF=999999;
int n,m,dp[N][2],c[N];
struct edge
{
int v,nx;
}e[M];
int ne,hd[N];
bool v[N];
void Build(int u,int v)
{
++ne,e[ne]=(edge){v,hd[u]},hd[u]=ne;
}
void Read()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;++i) scanf("%d",&c[i]);
int u,v;
for(int i=1;i<m;++i)
{
scanf("%d%d",&u,&v);
Build(u,v),Build(v,u);
}
}
void Init()
{
for(int i=1;i<=m;++i)
dp[i][0]=dp[i][1]=1;
for(int i=1;i<=n;++i)
dp[i][!c[i]]=INF;
}
void Tree_DP(int id)
{
v[id]=1;
for(int i=hd[id];i;i=e[i].nx)
if(!v[e[i].v])
{
Tree_DP(e[i].v);
dp[id][0]+=min(dp[e[i].v][0]-1,dp[e[i].v][1]),
dp[id][1]+=min(dp[e[i].v][1]-1,dp[e[i].v][0]);
}
}
int main()
{
Read(),Init(),Tree_DP(n+1),printf("%d
",min(dp[n+1][0],dp[n+1][1]));
return 0;
}