思路主要是紫书 上的,我在这里说一下建树的细节。
#include <vector>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
vector<int> son[10005];
int dp[10005][3];
int vis[10005];
//int have[10005];
const int INF = 1<<29;
void search(int u)
{
vis[u] = 1;
dp[u][0] = 0;
dp[u][1] = 0;
dp[u][2] = 1;
if(son[u].empty())
{
dp[u][0] = INF;
return ;
}
for(int i = 0; i < son[u].size(); i++)
{
if(vis[son[u][i]]) continue; //从树根开始向下遍历,一旦这个节点用过了,就不能再用了,否则就会出现遍历一个节点的儿子时遍历到这个节点的父亲的情况
if(!vis[son[u][i]]) search(son[u][i]);
dp[u][1] += dp[son[u][i]][0];
dp[u][2] += min(dp[son[u][i]][2], dp[son[u][i]][1]);
}
int sum = 0;
for(int i = 0; i < son[u].size(); i++)
sum += dp[son[u][i]][0];
dp[u][0] = sum + dp[son[u][0]][2] - dp[son[u][0]][0];
for(int i = 0; i < son[u].size(); i++)
dp[u][0] = min(dp[u][0], sum + dp[son[u][i]][2] - dp[son[u][i]][0]);
return ;
}
int main()
{
//freopen("ztest.txt","r",stdin);
//freopen("zans.txt","w",stdout);
int n, root;
int flagg = 1;
while(scanf("%d",&n))
{
for(int i = 0; i <= n; i++)
son[i].clear();
int flag = 1, temp1, temp2;
memset(vis, 0, sizeof(vis));
//memset(have, 0, sizeof(have));
while(scanf("%d",&temp1) && temp1)
{
if(flag == 1)
{
root = temp1;
flag = 0;
}
if(temp1 == -1) {flagg = 0; break;}
scanf("%d",&temp2);
son[temp1].push_back(temp2); //这时建立的树中,儿子的儿子中有父亲,vis[]解决了这个问题
son[temp2].push_back(temp1);
}
search(root);
//for(int i = 1; i <= n; i++)
// printf("%d %d %d\n",dp[i][0],dp[i][1],dp[i][2]);
printf("%d\n",min(dp[root][2], dp[root][0]));
if(!flagg) break;
}
return 0;
}