/*---UVa 1218 - Perfect Service ---首先对状态进行划分: ---dp[u][0]:u是服务器,则u的子节点可以是也可以不是服务器 ---dp[u][1]:u不是服务器,但u的父节点是服务器,则u的所有儿子节点都不是服务器 ---dp[u][2]:u和u的父亲都不是服务器,则u的儿子恰好有一个是服务器 ---状态转移方程: ---dp[u][0]=sum{min(dp[v][0],dp[v][1])}+1 ---dp[u][1]=sum(dp[v][2]); ---对于状态dp[u][2],计算略微复杂,这个状态说明u的儿子节点中恰好有一个是服务器,于是需要枚举每一个儿子节点是服务器 ---剩下儿子不是服务器的情况,考虑到d(u,1)=sum(dp[v][2]),所以每次枚举时,不必再累加子节点不是服务器的情况,因为这样 ---会使得计算一个节点复杂度达到O(k^2),k是u的子节点个数,可以:dp[u][2]=min(dp[u][1]-dp[v][2]+dp[v][0]),枚举v即可 ---在实现时,首先递归的构造有根树。然后可以采用记忆化搜索。 ---初始化问题,若u是叶子节点,dp[u][0]=1,dp[u][1]=0,dp[u][2]=INF,服务器个数不会超过10000,所以为了保准累加结果不溢出 ---可以将INF设置为10000. */ #define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<algorithm> #include<string.h> #include<vector> using namespace std; #define INF 10000+10; const int maxn = 10000 + 10; int d[maxn][3]; int parent[maxn]; vector<int>vec[maxn]; //构造有根树 void dfs(int u, int fa){ parent[u] = fa; for (int i = 0; i < vec[u].size(); i++){ int v = vec[u][i]; if (v != fa) dfs(v, u); } } int dp(int u, int k){ int&ans = d[u][k]; if (ans >= 0)return ans; int n = vec[u].size(); if (k == 0)ans = 1; else if (k == 1)ans = 0; else ans = INF; if (n == 1 && parent[u] == vec[u][0]){ //叶节点 return ans; } for (int i = 0; i < n; i++){ int v = vec[u][i]; if (v == parent[u])continue; //v是u的父节点,则跳过 if (k == 0)ans += min(dp(v, 0), dp(v, 1)); else if (k == 1) ans += dp(v, 2); else ans = min(ans, dp(u, 1) - dp(v, 2) + dp(v, 0)); } return ans; } int main(){ int n, i,u,v; while (scanf("%d", &n)){ for (i = 0; i <= n; i++)vec[i].clear(); for (i = 1; i < n; i++){ scanf("%d%d", &u, &v); u--, v--; vec[u].push_back(v); vec[v].push_back(u); } scanf("%d", &v); dfs(0, -1); vec[0].push_back(-1); memset(d, -1, sizeof(d)); int ans = min(dp(0, 0), dp(0, 2)); printf("%d ", ans); if (v == -1)break; } return 0; }