题目来源:https://vjudge.net/problem/UVA-1220
题目描述:紫书上树形dp的第二个例题,在树的最大独立子集上加上了一个判断唯一性。所以两个同时dp即可,转移方法如下:
对于最大独立子集:
1.dp[u][1]表示选了u这个节点,则子节点不能选,dp[u][1]+=dp[v][0];
2.dp[u][0]表示没选u这个节点,那么子节点可选可不选,取其中最大的即可,dp[u][0]+=max(dp[v][1],dp[v][0])
对于唯一性判断,用f[i][j]表示,f[u][0]=0表式唯一,f[u][1]表示不唯一,转移有下两个式子
1.当且仅当u的子节点f[v][0]=1全部成立时,f[u][1]才是1;
2.如果某个dp[v][0]==dp[v][1],则不唯一;如果max取到的对应的f为0,方案也不唯一,如dp[v][0]>dp[v][1],且f[v][0]=0
这样,套用最大独立子集的模板,加上f数组判断,就能实现递推了,。
细节看代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;
/*
注意f的转移方式,有两种:
1.当且仅当所有子节点的f[v][0]=0时,f[root][0]=0(这里的0表示唯一)
2.如果出现子节点dp[v][0]==dp[v][1],则f[root][0]=0
*/
const int maxn=205;
vector<int>G[maxn];
int n;
int dp[maxn][2];//表示选或不选
int f[maxn][2];//表示是否唯一
map<string,int>mp;
string s1,s2;
void init()
{
for(int i=0;i<maxn;i++)G[i].clear();
memset(dp,0,sizeof(dp));
memset(f,0,sizeof(f));
mp.clear();
}
void dfs(int root)
{
if(G[root].size()==0)
{
dp[root][0]=0;
dp[root][1]=1;
return;
}
int size=G[root].size();
for(int i=0;i<size;i++)
{
int v=G[root][i];
dfs(v);
if(f[v][0]==1)//子节点不唯一,父节点绝对不唯一
f[root][1]=1;
dp[root][1]+=dp[v][0];//这一层选了,儿子层都不能选
if(dp[v][0]>dp[v][1])
{
dp[root][0]+=dp[v][0];
if(f[v][0]==1)
f[root][1]=1;
}
else
{
dp[root][0]+=dp[v][1];
if(dp[v][1]==dp[v][0]||f[v][1])//两者相等也是不唯一的另一个条件
f[root][0]=1;
}
}
dp[root][1]++;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
init();
cin>>s1;
int top=0;
mp[s1]=top++;
for(int i=1;i<=n-1;i++)
{
cin>>s1>>s2;
if(mp.find(s1)==mp.end())
mp[s1]=top++;
if(mp.find(s2)==mp.end())
mp[s2]=top++;
G[mp[s2]].push_back(mp[s1]);
}
dfs(0);
if(dp[0][1]==dp[0][0])
{
printf("%d No\n",dp[0][1]);
}
else if(dp[0][1]>dp[0][0])
{
printf("%d ",dp[0][1]);
if(f[0][1])
printf("No\n");
else
printf("Yes\n");
}
else
{
printf("%d ",dp[0][0]);
if(f[0][0])
printf("No\n");
else
printf("Yes\n");
}
}
return 0;
}