题目大意:
给一棵树,在树上选点安装服务器,要求每个非服务器的点有且仅有一个服务器与之相连,求服务器的最少数量
树上的动态规划,仍然按照每个节点的情况分类:
1.f[u][0]表示u是服务器,那么每个子节点可以是也可以不是
2.f[u][1]表示u不是服务器,而u的父亲是服务器,则u的所有子节点都不是服务器
3.f[u][2]表示u和父亲都不是服务器,则u恰好有一个儿子是服务器
那么可以得到以下转移方程:(v代表u的所有字节点)
f[u][0]=sum{min(f[v][1],f[v][0])}+1
f[u][1]=sum{f[v][2]}
f[u][2]较为复杂,我们需要枚举每一个v,选它的f[v][0],然后加上其他儿子节点的f[v][2],不过由于f[u][1]求出了sum{f[v][2]},我们可以利用f[u][1]来写状态转移方程:f[u][2]=min(f[u][1]-f[v][2]+f[v][1])
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=1e4+5;
const int inf=1e4+5;
int f[maxn][5],n,x,y;
struct Edge{
int from,to;
Edge(int from,int to):from(from),to(to){}
};
vector<Edge>edges;
vector<int>G[maxn];
void clear(){
for(int i=1;i<maxn;i++)G[i].clear();
edges.clear();
memset(f,0,sizeof(f));
}
void addedge(int from,int to){
edges.push_back((Edge){from,to});
G[from].push_back(edges.size()-1);
}
void dfs(int u,int fa){
if(G[u].size()==1&&edges[G[u][0]].to==fa){
f[u][0]=1;
f[u][1]=0;
f[u][2]=inf;//对叶子节点赋初值
return ;
}
f[u][2]=inf;
for(int i=0;i<G[u].size();i++){
int e=G[u][i];
int v=edges[e].to;
if(v==fa)continue;
dfs(v,u);
f[u][0]+=min(f[v][0],f[v][1]);
f[u][1]+=f[v][2];
f[u][2]=min(f[u][2],f[u][1]-f[v][2]+f[v][0]);
}
f[u][0]++;
}
int main(){
while(scanf("%d",&n)==1){
if(n==-1)return 0;
if(n==0)scanf("%d",&n);
clear();
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
dfs(1,0);
printf("%d\n",min(f[1][0],f[1][2]));
}
}