题意:
公司里有n个人形成一个树状结构,即除了老板以外每个员工都有唯一的直属上司。要求选尽量多的人,但不能同时选择一个人和他的直属上司。输出最多能选多少人并判断是否唯一。
思路:树的最大独立集问题。就是需要额外判定是否是唯一的。
d[u][1]表示以u为根的子树中,选u点能得到的最大人数,f[u][1]判断这种方案是否唯一。
d[u][0]表示以u为根的子树中,不选u点能得到的最大人数,f[u][0]判断这种方案是否唯一。
有了这个状态定义,转移就不难了。
对于d[u][1],因为u选了,所以u的子结点都不能选,所以此时d[u][1]=sum(d[v][0]|v是u的子节点)。只有当f[v][0]全都唯一时,f[u][1]才唯一。
对于d[u][0],因为u没有选,所以此时u的子节点可选可不选,挑选出最大的那个。转移方程就是d[u][0]=sum(max(d[v][0],d[v][1]))。唯一性判定:如果有某个子节点d[s][0] = d[s][1],取不取都可以,说明不唯一;其次,对于max取到的那个值对应的 f = 0 的话,也不唯一。
少写了一行,卡了40min,我也很无奈呀哈哈。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <map>
#include <vector>
using namespace std;
const int N = 200+5;
vector<int> son[N];
map<string, int> mp;
int d[N][2], f[N][2], n;
void init(){
mp.clear();
for(int i = 0; i < n; ++i) {
son[i].clear();
f[i][0] = 1; f[i][1] = 1;
}
memset(d,0,sizeof(d));
string s1 , s2 ;
int cnt = 0;
cin>>s1;
//printf("%s\n",s1);
mp[s1] = cnt++;
for(int i = 1; i < n; ++i){
cin>>s1; cin>>s2;
if(mp.find(s1) == mp.end()) mp[s1] = cnt++;
if(mp.find(s2) == mp.end()) mp[s2] = cnt++;
son[mp[s2]].push_back(mp[s1]);
}
}
void solve(int u){
if(son[u].empty()) {
d[u][1] = 1; d[u][0] = 0;
return;
}
int k = son[u].size();
for(int i = 0; i < k; ++i){
int s = son[u][i];
solve( s ); // 递归计算孩子节点
// 如果选u
d[u][1] += d[s][0]; //不能选他的孩子
if (!f[s][0]) f[u][1] = 0; //一旦有子节点不唯一,它也不唯一
// 如果不选 u,孩子可选可不选,d[u] += max( d[s][0], d[s][1] );
if(d[s][0] > d[s][1]){
d[u][0]+= d[s][0];
if(f[s][0] == 0) f[u][0] = 0;
}
else {
d[u][0]+= d[s][1];
if(f[s][1] == 0) f[u][0] = 0;
if(d[s][0] == d[s][1]) f[u][0] = 0;
}
}
++d[u][1]; // 加上u自身
}
void print_ans(){
if(d[0][1] > d[0][0]){
printf("%d ",d[0][1]);
if(f[0][1])
printf("Yes\n");
else
printf("No\n");
}
else if(d[0][1] < d[0][0]){
printf("%d ",d[0][0]);
if(f[0][0])
printf("Yes\n");
else
printf("No\n");
}
else{
printf("%d ",d[0][1]);
printf("No\n");
}
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n) == 1&&n){
init();
solve(0);
print_ans();
}
return 0;
}