其他题解只给了转移方程而没有给清楚为什么是这样的 我在这就简要的分享一下自己的想法吧

首先这是一棵树

我们定义 三种状态:

0.我自己不选,我有儿子选

1.我自己不选,我父亲选

2.我自己选

0状态需要建立反悔机制,因为我只需要一个儿子选,

先推导 dp[x][0]=min(dp[v][0],dp[v][2]); 注意:因为当前节点是不选的,所以他儿子是父亲选择的状态不应该列入考核范围内。

这个式子推出来后可能照成的结果是儿子都没选 那我不就凉了 建立反悔,

t=min(t,dp[v][2]-min(dp[v][0],dp[v][2])) 保证一定有一个是儿子选了的,而且如果不本身就有儿子已经选了的状态t会为0

1状态是不选 所以不能接受儿子对他的依赖 所以 dp[x][1]+=min(dp[v][0],dp[v][2])

2状态呢是自己要选 儿子怎样都可以 dp[x][2]+=min(dp[v][0],min(dp[v][1],dp[v][2]))

于是我们就可以开始愉快的写代码了

可能大家会有一个疑问 如果是叶子节点,不就不能产生儿子选的状态了吗??

我们建立的反悔机制在进入时给的是极大值,最后会加上去,如果本身就很大,没有更新就加上去了 肯定不会有点选的

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stack>
#include<cmath>
#define LL long long
using namespace std;
inline void read(int &x){
    x=0;int f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();    }
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x*=f;
}
struct node{
    int v,nxt;
}e[10010<<1];
int head[10010],tot;
inline void add(int x,int y)
{
    tot++;
    e[tot].v = y;
    e[tot].nxt = head[x];
    head[x] = tot;
}
int dp[10010][3];//0不选 儿子选  1不选 父亲选 2自己选 
inline void dfs(int x,int fa)
{
    dp[x][0]=0;
    dp[x][1]=0;
    dp[x][2]=1;
    int t=99999999;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int v = e[i].v;
        if(v == fa) continue;
        dfs(v,x);
        dp[x][0]+=min(dp[v][0],dp[v][2]);
        t=min(t,dp[v][2]-min(dp[v][0],dp[v][2]));
        dp[x][1]+=min(dp[v][0],dp[v][2]);//父亲选 儿子不是父亲选随便 
        dp[x][2]+=min(dp[v][0],min(dp[v][1],dp[v][2]));//我选 
    }
    dp[x][0]+=t;
} 
int n,m,a,b;
int main()  
{     
    read(n);
    for(int i=1;i<n;i++)
        read(a),read(b),add(a,b),add(b,a);
    dfs(1,0);
    printf("%d",min(dp[1][0],dp[1][2]));
    return 0;
}