HDU 2412 / POJ 3342 Party at Hali-Bula(树形DP+判断多解)

题目大意

多组输入,当N为0时输入终止。
每组数据的第一行为最大的Boss,第2-n行中每一行包含两个字符串,代表员工A和B,且A的直接上级为B。当A参加聚会时,A的直接上级和直接下级都不能参加。求出能够参加聚会的最大人数并说明是否存在多组解。

解题思路

经典的树形DP题目,难点并不在求解最大值的过程中,而在判断多解的过程中。
由于题目中的输入为字符串,所以可以使用map使字符串和数字形成一对一的映射,便于处理。

转移过程:

主体遍历过程是DFS的过程,当某个点未被访问且为当前根节点的下属时,递归的调用该函数,将该点作为根节点处理,并在回溯的过程中进行状态转移

转移方程:

dp[now][1]+=dp[i][0];
dp[now][0]+=max(dp[i][1],dp[i][0]);
主要思路就是当选择当前根节点时,其子节点均不能选择,当不选择当前根节点时,则在是否选择子节点中选择具有最大值的情况。

判断多解:

我们先来分析一下多解产生的必要条件:

当有多解的情况产生时,一定有一个点满足dp[i][1]==dp[i][0],由于该状态是由子节点产生的,所以无需再考虑其子节点的情况,只要考虑父节点就好,其父节点一定满足dp[j][0]>dp[j][1]。即不去的时候更优。很容易理解,若父节点去了则该节点一定不去,这样就相当于在两种解中选了特定的一种,而只有父节点不去的时候该点的两种方案结果相同,才满足多解的条件。当该节点为总根节点的时候则无需考虑其父节点的情况,直接满足多解的条件。

附上代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <string>
using namespace std;
int n,boss[1005],vis[1005],dp[1005][2],mmax=0;
bool flag=true;
void solve(int now)
{
    vis[now]=1;
    dp[now][1]=1;
    for(int i=1;i<=n;i++){
        if(boss[i]==now&&!vis[i]){
            solve(i);
            dp[now][1]+=dp[i][0];
            dp[now][0]+=max(dp[i][0],dp[i][1]);
        }
    }
}
int main()
{
    while(~scanf("%d",&n),n!=0)
    {
        mmax=0;
        flag=true;
        map<string,int> compare;
        char root[1005],peo1[1005],peo2[1005];
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        memset(boss,0,sizeof(boss));
        int cont=2;boss[1]=1;
        scanf("%s",root);
        compare[root]=1;
        for(int i=1;i<n;i++){
            scanf("%s %s",peo1,peo2);
            if(!compare.count(peo1))
                compare[peo1]=cont++;
            if(!compare.count(peo2))
                compare[peo2]=cont++;
            boss[compare[peo1]]=compare[peo2];
        }
        solve(1);
        mmax=max(dp[1][0],dp[1][1]);
        for(int i=1;i<=n;i++){
            if(dp[i][0]>dp[i][1])
                for(int j=1;j<=n;j++){
                    if(boss[j]==i&&dp[j][0]==dp[j][1])
                    {
                        flag=false;break;
                    }
                }
            if(!flag)break;
        }
        if(flag&&dp[1][0]!=dp[1][1])
            printf("%d Yes\n",mmax);
        else
            printf("%d No\n",mmax);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值