- 給你一棵有 個點的樹及其樹根,每個節點用一個英文字符串表示。
- 你需要從中選擇儘可能多的點,使得沒有一個點和它的父親同時被選擇。求最多可以選出多少個點。
- 在選出點儘可能多的前提下,輸出方案是否唯一。唯一,輸出
Yes
,不唯一,輸出No
。
一道樹形 題。
不考慮節點的表示,單單考慮如何求出解先。
DP
一大特色:問什麼就求什麼。它要你輸出些什麼,你就把每一項都抽象成一個狀態即可。
記 表示考慮以 爲根的子樹且 不選時最多可以選多少個點, 類似,表示是 選時的方案。類似的,記 表示是否唯一,下標的含義與 完全一致,值爲真爲唯一,假爲不唯一。
-
選,則它的兒子們都必須不選,所以:
故,只有當所有的 爲真時, 才爲真。
-
不選,其兒子可選可不選,所以:
考慮是否唯一:
-
當 時,不唯一。
-
當較大值對應的 值爲假時,不唯一。
-
考慮節點的表示,很簡單,用個 map
給每個節點一個整數編號即可。
vector<int> son[210];//兒子
map<string,int> Name;//編號
int f[210][2],n,t;//dp結果
bool unq[210][2];//是否唯一
inline void dp(int u,int fa){
f[u][0]=0;f[u][1]=unq[u][0]=1;
unq[u][1]=true;//先初始化數據
int son_number=son[u].size();
for(int i=0;i<son_number;i++){
register int to=son[u][i];
if (to==fa) continue;//重要
dp(to,u);//先遞歸計算子兒子
if (!unq[to][0]) unq[u][1]=0;
f[u][1]+=f[to][0];//u參加舞會
if (f[to][0]>f[to][1]){
f[u][0]+=f[to][0];
if (!unq[to][0])
unq[u][0]=false;
}
else if (f[to][0]<f[to][1]){
f[u][0]+=f[to][1];
if (!unq[to][1])
unq[u][0]=false;
}
else{
f[u][0]+=f[to][0];
unq[u][0]=false;
}
}
}
int main(){
while (~scanf("%d",&n)&&n){
Name.clear();//清空編號
for(int i=1;i<=n;i++)
son[i].clear();//init
register string name;
cin>>name;Name[name]=t=1;
for(int i=2;i<=n;i++){
register string n1,n2;
cin>>n1>>n2;//n1與n2有連邊
if (!Name[n1]) Name[n1]=++t;
if (!Name[n2]) Name[n2]=++t;
son[Name[n2]].push_back(Name[n1]);
son[Name[n1]].push_back(Name[n2]);
}
dp(1,0);//遞歸進行計算
if (f[1][1]>f[1][0]){
printf("%d ",f[1][1]);
if (unq[1][1]) puts("Yes");
else printf("No\n");
}//千億要注意最後的輸出
else if (f[1][0]>f[1][1]){
printf("%d ",f[1][0]);
if (unq[1][0]) puts("Yes");
else printf("No\n");
}
else printf("%d No\n",f[1][1]);
}
return 0;
}