题目链接:https://vjudge.net/contest/307650#problem/B
题目大意:

思路:

因为d(u, 1)和d(u, 2)对子节点来说只要一个区别
d(u, 1):所有子节点都不是服务器
d(u, 2):只有一个子节点是服务器
那么d(u, 1):自己不是服务器,所有子节点都不是服务器,那么他的子节点状态是d(v, 2)
d(u, 1)-d(v, 2)表示u节点现在没有考虑v节点的个数,现在我们希望v是服务器,那么就可以推出d(u, 2),如果v是服务器要加上d(v, 0)。
所以:d(u, 2) = d(u, 1) - d(v, 2) + d(v, 0)

思考:wa了一上午
原因1:初始化在开始前。
原因2:有返回值的函数记得return, 不然一直T或者RE。还一直找不到原因。

对考虑父节点的dp,递推的时候不用考虑父节点的状态,例如d(v, 1)v的父节点是服务器,是不是就要加1,其实不用,因为父节点推d(u, 1)的时候会加,只有dp转移没有问题就可以了。因为父节点在递推的时候会考虑子节点。

当算法复杂度需要优化时,看看之前的状态能不能推导出这个复杂的状态。
减少时间复杂度

#include<bits/stdc++.h>
#define LL long long
using namespace std;
vector<int> e[10010];
int dp[10010][3];
int n;
void dfs(int u, int fa)
{
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];
        if(v!=fa)
        {
            dfs(v, u);
            dp[u][0]+=min(dp[v][1], dp[v][0]);
            dp[u][1]+=dp[v][2];
            dp[u][2]=min(dp[u][1]-dp[v][2]+dp[v][0], dp[u][2]);
        }

    }
    dp[u][0]++;
}

int main()
{
    while(cin>>n)
    {
       // scanf("%d",&n);
        for(int i=0;i<=n+1;i++)
        {
            e[i].clear();
            dp[i][0]=dp[i][1]=0;
            dp[i][2]=n+1;
        }

        int x, y;

        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            e[x].push_back(y);
            e[y].push_back(x);
        }
        int k;
        scanf("%d",&k);
        dfs(1, -1);

        printf("%d\n", min(dp[1][0], dp[1][2]));

        if(k==-1)
        {
            break;
        }
    }

    return 0;
}