传送门:点击打开链接
dp好题,难点在于递推关系不太直观,需要静下来分析。
设A是每次取最大的,B是每次取最小的,Max模式是使结果尽可能大的安排方式,Min模式是使结果尽可能小的安排方式按题目的需求有4种状态:
A-Max
A-Min
B-MAx
B-Min
最直观的是A-Max和B-Min,以A-Max为例,要使得当前子树的取值最大,那么会把编号最大的点都放到这课子树上。并且A-Max和B-Min是一种对偶关系,满足Va+Vb=Cap+1(Cap是所有叶子节点的个数)
现在考虑剩下的两种情况,A-Min的所有子决策是B-Min,上面已经说过,B-Min在对于单独的一颗子树来说会把所有编号最小的点放在这课子树上。假设这样得到的值为V[s1],V[s2]..V[sk],但是A-Min并不是取这些值中的最大值,因为每个编号只能用一次。可以知道当V[s1]被满足时,所有编号小于等于V[s1]的点都被放到了这s1这课子树上,因而对于s2来说,可以取到的值就变成了V[s2]+V[s1]。所以最后可以得到A-Min=Sum(V[si])。
在Min模式下,所有A的取值方式被安排得尽可能平均,所以B-Min=min(V[si])
以上。
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<vector>
using namespace std;
vector<int> G[200005];
int dp[200005][2];
void dfs(int p){
if(G[p].size()==0){
dp[p][0]=1;
dp[p][1]=1;
return;
}
dp[p][0]=0;dp[p][1]=1000000000;
for(int i=0;i<G[p].size();i++){
int to=G[p][i];
dfs(to);
dp[p][0]+=dp[to][1];
dp[p][1]=min(dp[p][1],dp[to][0]);
}
}
int main(){
int n;
int i,j;
int x,y;
scanf("%d",&n);
for(i=1;i<n;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
}
int c=0;
for(i=1;i<=n;i++){
if(G[i].size()==0){
c++;
}
}
dfs(1);
printf("%d %d\n",c+1-dp[1][1],dp[1][0]);
return 0;
}