NC13249 黑白树(dp)
这道题有着贪心的性质在里面,首先我们观察题目的变量,可以想到设计dp表示以该节点为根节点的子树的最小次数是多少
但是这样是不够的。我们继续观察,发现这道题虽然叶子节点必须染色,但是染色的顺序是不定的,所以我们可以先找到哪种情况是最小的。
因此定义dp[u][1]和dp[u][2]分别表示已被染色的子节点能染色的最长距离和所有子节点传递过来的还能染色的最大距离
因为我们知道,如果dp[u][1]>0说明这个点可以不用加次数,反之就要取dp[u][2]的位置转移,这是最优的。
具体的来说,比如这个树是
1
2 3
4 5 6 7
这样的,递归终止肯定是叶子节点,并且这些点必须染色,之后我们来看1这个位置,假如从4,5,6,7涂完色的剩余距离还能染到1,那么自然最小的就是4次,加入不行,说明还至少需要一次的染色
那么我们就可以贪心地找染完之后还剩余距离最大的,比如3的值是5,2的值是3,1的值是3,那么我们肯定选择染3这个位置,那么6 7就可以后染,避免影响到3.虽然从这个树上看不出区别
但是如果上面还有点,那就能看出效果了。
#include<iostream> #include<algorithm> #include<stack> #include<vector> #include<cstring> using namespace std; typedef long long ll; const int N=1e5+10; const int mod=1e9+7; int f[N][3]; int k[N]; int h[N],ne[N],e[N],idx; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void dfs(int u){ if(h[u]==-1){ f[u][0]=1; f[u][1]=k[u]; f[u][2]=k[u]; return ; } f[u][1]=0; f[u][2]=k[u]; for(int i=h[u];i!=-1;i=ne[i]){ int j=e[i]; dfs(j); f[u][0]+=f[j][0]; f[u][1]=max(f[u][1],f[j][1]-1); f[u][2]=max(f[u][2],f[j][2]-1); } if(f[u][1]<=0){ f[u][0]++; f[u][1]=f[u][2]; } } int main(){ int i; int n; cin>>n; memset(h,-1,sizeof h); for(i=2;i<=n;i++){ int u,v; scanf("%d",&u); add(u,i); } for(i=1;i<=n;i++){ scanf("%d",&k[i]); } dfs(1); cout<<f[1][0]<<endl; }
没有人不辛苦,只有人不喊疼