树形DP解 POJ3342-Party at Hali-Bula

参考: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>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值