Hali-Bula的晚会(UVa1220)详细题解

累加器传送门:

http://blog.csdn.net/NOIAu/article/details/71775000

题目传送门:https://vjudge.net/problem/UVA-1220

题目大意是说一个公司的员工成树形分布,每个人只有一个直属上司,现在要开个party,不能让一个人和他的直接老板同时出现在party上,问最多能选多少人,并问选择是否唯一

用dp[i][j]表示最大人数,其中i为第i个点,其中j可以为0或者为1,表示第i个人选或者不选,即选或者不选i的以i为根的子树的最优值,另一个f[i][j]表示唯不唯一,i和j的含义dp数组一样


dp[i][1]的计算

因为选了第i个点,所以i的直接下属都不能选,所以dp[i][1]的值应该为所有下属不选(dp[son][0])的值之和,再加上1(因为选了i)


f[i][1]的计算

当所有的f[son][0]都唯一的时候,f[i][1]唯一


dp[i][0]的计算

不选i,所以他的直接下属是自由的,所以可以邀请也可以不邀请,所以应该把每个son节点的选(dp[son][1])或者不选(dp[son][0])的大的那个值,将每一个son的大的那个值加起来即可


f[i][0]的计算

如果选一个子点(dp[son][1])和不选一个子点(dp[son][0])所邀请的人是一样的,所以此时方案不唯一,还有一种情况是如果在计算dp[i][0]的时候每一个son节点的max值选择的时候,选择的那个的f是不唯一的,那么f[i][0]是不唯一的,比如(dp[son][0]>dp[son][1],且f[son][0]不唯一,则f[i][0]不唯一)

下面上代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<vector>
#define MAXN 300+10
using namespace std;

map<string,int>m;
vector<int>v[MAXN];
int tail;
int dp[MAXN][5],f[MAXN][5];
int n;
bool init(){
    m.clear();
    tail=0;
    scanf("%d",&n);
    if(n==0) return false;
    for(int i=0;i<n;i++) v[i].clear();
    string s,ss;
    cin>>s;
    m[s]=tail++;
    for(int i=1;i<=n-1;i++){
        cin>>s;
        cin>>ss;
        if(m.find(s)==m.end()){
            m[s]=tail++;
        }
        if(m.find(ss)==m.end()){
            m[ss]=tail++;
        }
        v[m[ss]].push_back(m[s]);
    }
    for(int i=0;i<=n;i++){
        f[i][0]=1;
        f[i][1]=1;
    }
    memset(dp,0,sizeof(dp));
    return true;
}

void dpp(int u){
    if(v[u].empty()){
        dp[u][1]=1;
        dp[u][0]=0;
        return ;
    }
    int son=v[u].size();
    for(int i=0;i<son;i++){
        int to=v[u][i];
        dpp(to);
        dp[u][1]+=dp[to][0];
        if(dp[to][0]>dp[to][1]){
            dp[u][0]+=dp[to][0];
            if(f[to][0]==0) f[u][0]=0;
        }else{
            dp[u][0]+=dp[to][1];
            if(f[to][1]==0) f[u][0]=0;
            if(dp[to][0]==dp[to][1]) f[u][0]=0;
        }
    }
    dp[u][1]++;
}

void print(){
    if(dp[0][1]>dp[0][0]){
        printf("%d ",dp[0][1]);
        if(f[0][1]){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
    }
    if(dp[0][1]<dp[0][0]){
        printf("%d ",dp[0][0]);
        if(f[0][0]){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
    }
    if(dp[0][1]==dp[0][0]){
        printf("%d ",dp[0][0]);
        printf("No\n");
    }
}

int main(){
    freopen("in.in","r",stdin);
    while(init()){
        dpp(0);
        print();
    }
    return 0;
}

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值