此题是经典树形DP题,没有上司的晚会。
公司所有人员之间的关系可以用一个树来表示,任何一个人(除了最大的boss外)都有且只有一个直接上司,现要开一个Party,邀请尽量多的人参加,要求任何人不得与直接上司同时参加。
抽象后的数学模型:在一棵树中,最多能选取多少结点,使得任何结点之间没有直接连边。
状态设计:d[i][0]表示在以i为根结点的子树中,在不选结点i的情况下最多能选取的结点数目,d[i][1]表示在以i为根结点的子树中,在选取结点i的情况下最多能选取的结点数目。
状态转移:d[i][0]=(max(d[j][0],d[j][1])),j为i的儿子结点,d[i][1]=∑(d[j][0]),j为i的儿子结点。
边界条件:当i没有儿子结点时,也即i为叶子结点,此时d[i][0]=0,d[i][1]=1。
需要注意的是判断最后的解是否唯一,此时可以用一个unique数组来同步记录,unique[i][0]记录以i为根结点且不选i时取最多的点是否唯一,unique[i][1]记录以i为根结点且选i时取最多结点是否唯一。在状态转移时同步更新unique数组。

#include <stdio.h> #include <string.h> #include <vector> using namespace std; #define MAX(a,b) ((a)>(b)?(a):(b)) #define N 200 #define LEN 101 char name[N][LEN]; char s[LEN]; int n,top; int p[N],d[N][2]; char unique[N][2]; vector<int> son[N]; int find() { for(int i=0;i<top;i++) if(strcmp(name[i],s)==0) return i; strcpy(name[top++],s); return top-1; } int dp(int i,int f) { int j,k,ans,cnt=son[i].size(); if(d[i][f]!=-1) return d[i][f]; if(f) { ans=1; for(k=0;k<cnt;k++) { j=son[i][k]; ans+=dp(j,0),unique[i][f]&=unique[j][0]; } } else { ans=0; for(k=0;k<cnt;k++) { j=son[i][k]; ans+=MAX(dp(j,0),dp(j,1)); if(dp(j,0)==dp(j,1)) unique[i][f]=false; if(dp(j,0)>dp(j,1)) unique[i][f]&=unique[j][0]; else unique[i][f]&=unique[j][1]; } } return d[i][f]=ans; } void clear() { for(int i=0;i<n;i++) son[i].clear(); } int main() { int i,a,b; bool flag; while(scanf("%d",&n)&&n) { clear(); top=0; scanf("%s",s); p[find()]=-1; for(i=1;i<n;i++) { scanf("%s",s); a=find(); scanf("%s",s); b=find(); p[a]=b; son[b].push_back(a); } memset(d,-1,sizeof(d[0])*n); memset(unique,1,sizeof(unique[0])*n); a=dp(0,0); b=dp(0,1); if(a==b) flag=false; else if(a>b) flag=unique[0][0]; else flag=unique[0][1]; printf("%d %s\n",MAX(a,b),flag?"Yes":"No"); } return 0; }