传送门
【题目分析】
N个点,N-1条边,那么这就是一棵树,再看看这种父亲与儿子之间的相互影响的关系,那么就锁定树形DP做法。
当我们确定了一个点为根的时候,所有点的父子关系都确定了。
考虑一个点,有三种选择:1.自己建 2.自己不建,由儿子将自己覆盖 3.自己不建,由父亲覆盖,所以开dp[MAXN][3]记录3个状态的最优解。
考虑转移。如果当前点选择自己建,那么他的儿子就有两种选择:1和3;
如果当前点选择自己不建由儿子覆盖,那么他的儿子中就至少有一个要选择建,所以递归处理儿子建和不建的最优解,再选择哪些儿子要建。
如果当前点被父亲覆盖,那么当前点就不用考虑,直接统计所有儿子最优解即可。
【代码~】
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e4+10;
const int MAXM=2e4+10;
const int INF=0x3f3f3f3f;
int n,cnt;
int head[MAXN];
int nxt[MAXM],to[MAXM];
int dp[MAXN][3],sum[MAXN];
int Read(){
int i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
void add(int x,int y){
nxt[cnt]=head[x];
head[x]=cnt;
to[cnt]=y;
cnt++;
}
int DP(int u,int fa,int stau){
if(dp[u][stau]!=-1)
return dp[u][stau];
if(stau==0){
dp[u][stau]=1;
for(int i=head[u];i!=-1;i=nxt[i]){
int v=to[i];
if(v==fa)
continue;
dp[u][stau]+=min(DP(v,u,0),DP(v,u,2));
}
}
if(stau==1){
dp[u][stau]=INF;
int total=0;
for(int i=head[u];i!=-1;i=nxt[i]){
int v=to[i];
if(v==fa)
continue;
sum[v]=min(DP(v,u,0),DP(v,u,1));
total+=sum[v];
}
for(int i=head[u];i!=-1;i=nxt[i]){
int v=to[i];
if(v==fa)
continue;
dp[u][stau]=min(dp[u][stau],DP(v,u,0)+total-sum[v]);
}
}
if(stau==2){
dp[u][stau]=0;
for(int i=head[u];i!=-1;i=nxt[i]){
int v=to[i];
if(v==fa)
continue;
dp[u][stau]+=min(DP(v,u,0),DP(v,u,1));
}
}
return dp[u][stau];
}
int main(){
memset(head,-1,sizeof(head));
memset(dp,-1,sizeof(dp));
n=Read();
for(int i=1;i<n;++i){
int x=Read(),y=Read();
add(x,y),add(y,x);
}
cout<<min(DP(1,-1,0),DP(1,-1,1));
return 0;
}