参考:http://blog.csdn.net/sdjzping/article/details/13131611
第一次接触树形DP的题目,这种题解题思路还挺固定的,深度优先搜索+动态规划。这点比较好理解,dp[i][j]保存节点i在状态j下的最多参加宴会人数,状态j其实就两种状态,也就是只有2种取值,0代表不参加,1代表参加。
首先赋初值,dp[i][0]=0,就结点i一人来说,不参加,人数为0;dp[i][1]=1,参加,就有一个人了。
当f参加时,dp[f][0] = dp[f][0] + Sigma max( dp[p][0] , dp[p][1] ) ,这里p是f的儿子结点。对于每个儿子结点,做父亲的都要考虑,各儿子拿出自己最好的状态(0和1两种情况。不一定他们参加,值就最大,可以不参加),由父亲汇总,(父亲自己是不参加的,初始状态值dp[f][0]为0)。这个Sigma代表求和,博客里不会打公式符号。
当f参加时,dp[f][1] =dp[f][1] + Sigma dp[p][0],这里父亲自己参加,dp[f][1]初值是1,此时儿子都不能参加,
这题难点可能就是判断结果方案是否唯一上面。看上面博客的这段代码开始怎么也不能理解,后来翻看别人的博客,总算是有些眉目。很多人这里求解都是另外设个和dp数组一样的二维布尔状态数组保存当前方案是否唯一,随着dp求解的同时一直状态转移到根结点得出结果,上面参考代码是在dp求解结束时,利用dp结果的特性来得出结论的。前者节省时间,后者节省空间,不过我更喜欢后者的简洁。它的大意:如果算到儿子结点时,方案有不唯一的,后面的不用算了,整个方案肯定都不唯一。看上面当f不参加时的状态方程:dp[f][0] = dp[f][0] + Sigma max( dp[p][0] , dp[p][1] ), 如果某个儿子结点p参不参加都一样,也就是dp[p][0]=dp[p][1],那可以判定,整个方案解是不唯一的。但是,这个前提是在计算父结点f时,你选用了这个不参加时的状态方程,而不是另一个f参加时的方程:dp[f][1] = dp[f][1] + Sigma dp[p][0]。那如何才能选这个方程呢,当然是f不参加时,总参加人数更多呗,也就是dp[f][0]>dp[f][1]。注意一点,这里判断都是基于儿子p判断f的,最后一步时,没考虑f结点自身,所以要稍微处理一下。讲到这,代码就很好理解了。
<span style="font-size:12px;">#include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<map> #include<vector> using namespace std; vector<int> vec[205]; map<string,int> mp; char str[205][105]; char tmp[105]; char a[105],b[105]; int dp[205][2]; int k; void dfs(int root) { for(int i=0; i<vec[root].size(); i++) { int v=vec[root][i]; dfs(v); dp[root][0]+=max(dp[v][0],dp[v][1]); dp[root][1]+=dp[v][0]; } } int main() { //freopen("in.txt","r", stdin); int n; while(scanf("%d",&n)!=EOF) { if(n==0) break; k=0; mp.clear(); scanf("%s",tmp); mp[tmp]=k++; for(int i=0; i<n; i++) { vec[i].clear(); dp[i][0]=0; //初始时,结点i不被选中时的结点数为0,选中时为1 dp[i][1]=1; } for(int i=1; i<n; i++) { scanf("%s%s",a,b); if(mp.find(a)==mp.end()) mp[a]=k++; if(mp.find(b)==mp.end()) mp[b]=k++; vec[mp[b]].push_back(mp[a]); } dfs(0); //下面代码判断是不是唯一的方案 int flag=1; for(int i=0; i<n; i++) { if(dp[i][0]>dp[i][1]) { for(int j=0; j<vec[i].size(); j++) { int v=vec[i][j]; if(dp[v][0]==dp[v][1]) { flag=0; break; } } } if(flag==0) break; } if(flag==0 || dp[0][0]==dp[0][1])//注意判断条件, printf("%d No\n",max(dp[0][0],dp[0][1])); else printf("%d Yes\n",max(dp[0][0],dp[0][1])); } return 0; } /* 6 Jason Jack Jason Joe Jack Jill Jason John Jack Jim Jill 2 Ming Cho Ming 0 */</span>