题意:
思路:
注意:
- d[u][2]不能设为真的无穷大,因为涉及累加,会溢出。应该设一个它可能取到的最大值n。
- 然后就是,刚开始用vis数组标记,模拟dfs来遍历树,发现不行,因为在求d[u][2]的时候之前的孩子节点已经被标记访问过了,导致d[u][2]根本算不了,应该在参数列表加一个参数表示父节点。
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e5+5;
vector<int> G[N];
int d[N][3], n;
void solve(int u, int dad){
//printf("%d ",u);
//vis[u] = 1;
d[u][0] = 1; d[u][1] = 0;
d[u][2] = n; // d[u][2]是不可能状态,否则叶子节点没有服务器相连了,置为无穷大。
int k = G[u].size();
if(k == 0){
d[u][2] = 0; return;
}
for(int i = 0; i < k; ++i){
int s = G[u][i];
if(s == dad) continue;
solve( s, u );
// 选 u , 子可选可不选,选代价最少的
d[u][0] += min(d[s][0], d[s][1]);
// 不选 u
d[u][1] += d[s][2]; // 如果u的父亲被选了,那么u的儿子都不能选
//d[u][2] += min();
}
for(int i = 0; i < k; ++i){
int s = G[u][i];
if(s == dad) continue;
d[u][2] = min(d[u][2], d[u][1] + d[s][0] - d[s][2]) ;// 如果u和u的父亲都没被选,那么u的儿子当中一定要有且仅有1个被选。
}
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n) == 1&&n){
for(int i = 1; i <= n; ++i) G[i].clear();
//memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
for(int i = 0; i < n-1; ++i){
int a,b; scanf("%d %d",&a,&b);
G[b].push_back(a); G[a].push_back(b);
}
solve(1,-1);
printf("%d\n",min(d[1][0],d[1][2]));
int e; scanf("%d",&e);
if(e == -1) break;
}
return 0;
}