题目描述
树形dp题。
设:
d(i,k)
表示以i为子树满足题意的需要最少服务器数量。
且:
d(i,0)
表示i和i的父亲节点都不装服务器;
d(i,1)
表示i的父亲装了但i不装;
d(i,2)
表示i节点装服务器。
则:
d(i,0)=min(∑kϵson(i)d(k,0)+d(j,2)|k≠j)
d(i,1)=∑jϵson(i)d(j,0)
d(i,2)=∑jϵson(i)min(d(j,1),d(j,2))+1
观察d(i,0)可以化简为:
d(i,0)=min(d(i,1)−d(j,0)+d(j,2))
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=10010;
int d[maxn][3],n;
vector<int> v[maxn];
void dp(int u,int fa){
d[u][0]=100000;d[u][1]=0;d[u][2]=1;
for(int i=0;i<v[u].size();i++) if(v[u][i]!=fa){
int j=v[u][i];
dp(j,u);
d[u][1]+=d[j][0];
d[u][2]+=min(d[j][2],d[j][1]);
}
for(int i=0;i<v[u].size();i++) if(v[u][i]!=fa)
d[u][0]=min(d[u][0],d[u][1]-d[v[u][i]][0]+d[v[u][i]][2]);
}
int main(){
for(int a=0,b;a!=-1;){
scanf("%d",&n);
for(int i=0;i<=n;i++) v[i].clear();
while(scanf("%d",&a)==1&&a&&a!=-1){
scanf("%d",&b);
v[a].push_back(b);
v[b].push_back(a);
}
dp(1,0);
printf("%d\n",min(d[1][0],d[1][2]));
}
return 0;
}