题目背景
小v带萌萌的妹妹去玩,妹妹想去女仆咖啡馆,小v想去桌游吧。
妹妹:“我问你个问题,答不对你就做我一天的奴隶,答对了就今天我就全部听你的。”
小v:“全部都听!?”
妹妹:“嘻嘻嘻,你还是回答问题吧!”
于是小v为了自己一天的幸福,来向你求助。
题目描述
小v所在的世界被规划成了树形结构,每一个节点上都可以建一个女仆咖啡厅或者桌游吧或者什么都不建。在确定点1为根节点之后,规划局要求:对于每一个非叶子的节点i,设它子树(包括自己)中所有的女仆咖啡厅的数量为cafe[i],桌游吧数目为table[i],都有cafe[i]等于table[i]。
妹妹的问题是:这颗树最多能放多少个女仆咖啡厅。
输入输出格式
输入格式:
第一行,一个正整数N
第二至N行,每行两个正整数ui,vi,表示vi,ui有一条边。
输出格式:
只有一行,最多能放的女仆咖啡厅的个数。
为了写得方便,设f[i]为以i为根节点的子树最多能建的女仆咖啡厅的数量,L[i]为i的叶节点孩子的个数,N[i]为i的非叶节点孩子的集合。
因为对于每个节点,只有它的叶节点孩子和它自己可以随意建女仆咖啡厅和桌游吧,f[a](a∈N[i])是确定的,不能改动,所以f[i]=( l[i]+1个点上上能建的女仆咖啡厅的数量+
f[ i 的非叶节点孩子])
又因为cafe[i]==table[i],所以在每个节点与其叶节点孩子上最多能建(L[i]+1)/2个女仆咖啡厅。
则可以推出动态转移方程为:
F[i]={f[a]}+(L[i]+1)/2 (a∈N[i])
代码如下:
#include <cstdio>
#include <vector>
std::vector<int> con[100001];
int d[100001],s,t,n;
void push(int s,int t){ //怪异的邻接表
con[s].push_back(t);
}
int check(int x,int f) //DP函数
{
int tot=0,Ttot=1;
for(int k=0;k<con[x].size();k++){
int i=con[x][k];
if(i==f)continue; //如果是x的父节点就返回
if(con[i].size()==1)Ttot++; //如果是叶节点就记录
else tot+=check(i,x); //否则加上f[i]
}
return tot+Ttot/2;
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++) scanf("%d%d",&s,&t),push(s,t),push(t,s);
printf("%d",check(1,0));
}