題意:一個公司想舉辦聚會,但是爲了氣氛,如果邀請一個員工的話,是不會再邀請他的直接上司。這個公司的人員結構是樹形的,每個人至多有一名直接上司。給出這個公司的人員結構,求出能參加聚會的人數的最大值,同時判斷方案是否唯一。
思路:同樣的樹的最大獨立集,不同的是還需要判斷方案是否唯一。其實,如果我們求出了方案數,就可以知道方案是否是唯一的。
題目中是給出的每個人的名字,直接用map離散一下就可以得到數字了。
可以注意到,節點u和是否被選就能描述對應的狀態。
所以定義dp[u][0]爲不選節點u,以節點u爲根的子樹得到的最大獨立集,f[u][1]爲對應的方案數。
dp[u][1]爲選節點u,以節點u爲根的子樹得到的最大獨立集,f[u][0]爲對應的方案數。
下面考慮狀態轉移:
1.因爲dp[u][1]爲選擇節點u,那就不能選他的兒子節點,只能選擇dp[v][0]
所以:
2.dp[u][0]表示不選擇節點u,那對於其兒子節點,我們可以選也可以不選
所以:
方案計數爲:
初始條件:
最終答案,當dp[1][0] = dp[1][1] 或 取到最大值的方案數不唯一, 最終方案不唯一。
代碼如下:
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
const int MAX = 20000;
int N;
int to[MAX*2],nxt[MAX<<1],head[MAX],tot;
int dp[MAX][2],f[MAX][2];
map<string,int> ma;
void init()
{
memset(head,-1,sizeof(head));
tot = 0;
ma.clear();
}
void addedge(int u, int v)
{
to[tot] = v, nxt[tot] = head[u];
head[u] = tot++;
to[tot] = u, nxt[tot] = head[v];
head[v] = tot++;
}
int insert(string s)
{
if(ma[s] == 0)
return ma[s] = ma.size();
else
return ma[s];
}
void dfs(int u, int p)
{
dp[u][1] = 1,dp[u][0] = 0;
f[u][1] = f[u][0] = 1;
for(int i = head[u]; ~i; i = nxt[i]){
int v = to[i];
if(v == p) continue;
dfs(v,u);
dp[u][1] += dp[v][0];
f[u][1] *= f[v][0];
if(dp[v][0] > dp[v][1]){
dp[u][0] += dp[v][0];
f[u][0] *= f[v][0];
}
else if(dp[v][0] < dp[v][1]){
dp[u][0] += dp[v][1];
f[u][0] *= f[v][1];
}
else{
dp[u][0] += dp[v][1];
f[u][0] *= (f[v][1] + f[v][0]);
}
}
}
int main(void)
{
//freopen("input.txt","r",stdin);
while(cin>>N,N){
init();
string s1,s2;
cin>>s1;
int root = insert(s1);
for(int i = 0; i < N - 1; ++i){
cin>>s1>>s2;
int u = insert(s1),v = insert(s2);
addedge(u,v);
}
dfs(root,0);
int ans = max(dp[root][0],dp[root][1]);
cout << ans <<' ';
if(dp[root][0] == dp[root][1] || (ans == dp[root][0] && f[root][0] != 1)
|| (ans == dp[root][1] && f[root][1] != 1))
cout<<"No"<<endl;
else
cout <<"Yes"<<endl;;
}
return 0;
}