思路:
- 题目要 求好节点数最大时的最小值
- 好节点数是前提条件,那么就分段考虑, 先看哪些点是好节点, 在看好节点时,如何让值最小
- 贪心发现规律: 2个点之间不能同时好节点, 对于不是好节点的点,然他权值为1即可,好节点为儿子数
- dp转移: dp[i][0], 无所谓儿子是好是坏, 当儿子权值一样时, 就以num[b][0],num[b][1]为标准来更新这个num[i][0],选其小的. ------
- p[i][1], 儿子必须全是不好
- 注意初始化即可
- 再来一个dfs 记录ans即可.
#include <bits/stdc++.h> using namespace std; #define ri register int #define M 2000005 int n,m; int T; vector <int> p[M]; int vis[M]; int dp[M][3]; int num[M][3]; int val[M]; void dfs(int a) { vis[a]=1; for(ri i=0;i<p[a].size();i++) { int b=p[a][i]; if(vis[b]) continue; dfs(b); if(dp[b][0]>dp[b][1]) { dp[a][0]+=dp[b][0]; num[a][0]+=num[b][0]; } else if(dp[b][0]<dp[b][1]) dp[a][0]+=dp[b][1],num[a][0]+=num[b][1]; else{ dp[a][0]+=dp[b][0]; num[a][0]+=min(num[b][0],num[b][1]); } num[a][1]+=num[b][0]; dp[a][1]+=dp[b][0]; } num[a][0]++; num[a][1]+=p[a].size(); dp[a][1]++; } long long ans=0; void dfs2(int a,int t) { vis[a]=1; if(t==0) { ans++; val[a]=1; for(ri i=0;i<p[a].size();i++) { int b=p[a][i]; if(vis[b]) continue; if(dp[b][0]>dp[b][1]) { dfs2(b,0); } else if(dp[b][0]<dp[b][1]) dfs2(b,1); else{ if(num[b][0]<=num[b][1]) dfs2(b,0); else dfs2(b,1); } } } else { ans+=p[a].size(); val[a]=p[a].size(); for(ri i=0;i<p[a].size();i++) { int b=p[a][i]; if(vis[b]) continue; dfs2(b,0); } } } int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>n; for(ri i=1;i<n;i++) { int a,b; cin>>a>>b; p[a].push_back(b); p[b].push_back(a); } if(n==2) { cout<<"2 2\n"; cout<<"1 1"; return 0; } dfs(1); memset(vis,0,sizeof(vis)); if(dp[1][0]>dp[1][1]) { dfs2(1,0); } else if(dp[1][0]<dp[1][1]) dfs2(1,1); else{ if(num[1][0]<=num[1][1]) dfs2(1,0); else dfs2(1,1); } cout<<max(dp[1][0],dp[1][1])<<" "<<ans<<"\n"; for(ri i=1;i<=n;i++) cout<<val[i]<<" "; }
后记:
- 树上dp,关键是这个dp, 和 树上贡献的题 相互区分一下, 是一个包含关系. DP可以处理很多问题
- 有前提条件就分段做