【算法竞赛入门经典】树的最大独立集、树的唯一性问题 例题9-13 UVa1220

【算法竞赛入门经典】树的最大独立集、树的唯一性问题 例题9-13 UVa1220

例题UVa12186

Dear Contestant,
I’m going to have a party at my villa at Hali-Bula to celebrate my retirement from BCM. I wish I could invite all my co-workers, but imagine how an employee can enjoy a party when he finds his boss among the guests! So, I decide not to invite both an employee and his/her boss. The organizational hierarchy at BCM is such that nobody has more than one boss, and there is one and only one employee with no boss at all (the Big Boss)! Can I ask you to please write a program to determine the maximum number of guests so that no employee is invited when his/her boss is invited too? I’ve attached the list of employees and the organizational hierarchy of BCM.
Best, –Brian Bennett
P.S. I would be very grateful if your program can indicate whether the list of people is uniquely determined if I choose to invite the maximum number of guests with that condition.

Input

The input consists of multiple test cases. Each test case is started with a line containing an integer n (1 ≤ n ≤ 200), the number of BCM employees. The next line contains the name of the Big Boss only. Each of the following n−1 lines contains the name of an employee together with the name of his/her boss. All names are strings of at least one and at most 100 letters and are separated by blanks. The last line of each test case contains a single ‘0’.

Output

For each test case, write a single line containing a number indicating the maximum number of guests that can be invited according to the required condition, and a word ‘Yes’ or ‘No’, depending on whether the list of guests is unique in that case.

Sample Input

6
Jason
Jack Jason
Joe Jack
Jill Jason
John Jack
Jim Jill
2
Ming
Cho Ming
0

Sample Output

4 Yes
1 No

分析

首先显然字符串操作会很麻烦耗时,而且操作过程中和输出和字符串无关,不如给其编号。
其次,输入的同时,已经进行了建树(有根),即:每个节点的子节点是知道的。
此处用d[u][0]表示不选择节点u时,以u为根的子树的最大可选人数,那么d[u][1]则表示选择节点u的时候,以u为根的子树的最大可选人数。
同样的f[u][0]表示不选择节点u时,以u为根的子树的最大可选人数是否唯一,f[u][1]表示选择时的情况。
此时考虑状态转移方程:
1.选择节点u的时候,所有子节点必须不选d[u][1]=sum(d[v][0])+1 考虑独立性:只有在任意一个子节点f[v][0]不唯一的时候,f[u][1]才会不唯一。
2.不选择节点u的时候,子节点可选可不选 d[u][0]=sum(max(d[v][0],d[v][1])) 考虑独立性:当d[v][0]和d[v][1]值相同的时候,选择哪一个都行,那么就不唯一了。若是选择的其中大的那个不唯一,那么f[u][0]也不唯一。
根据以上,本题涉及重复计算,所以要使用记忆化搜索。
根节点的判唯一方式也类似与子节点。
另外:本题使用的是递归而不是递推来做,究其原因是因为不能保证递推从子节点开始。若是输入时能获知节点层次,那么直接从下往上递推显然效率更高。

样例实现代码

#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
#define maxn 200+5
using namespace std;
int n,cnt;
int d[maxn][2];
bool f[maxn][2];
vector<int>sons[maxn];
map<string,int>dict;
int ID(string &s){
    if(!dict.count(s))
        dict[s]=cnt++;
    return dict[s];
}
int dp(int n,int flag){
    d[n][flag]=flag;
    f[n][flag]=true;
    for(int i=0;i<sons[n].size();i++){
        int v=sons[n][i];
        if(flag==1){
            d[n][1]+=dp(v,0);
            if(!f[v][0])
                f[n][1]=false;
        }
        else{
            d[n][0]+=max(dp(v,0),dp(v,1));
            if(d[v][0]==d[v][1])
                f[n][0]=false;
            else if(d[v][0]>d[v][1]){
                if(!f[v][0])
                    f[n][0]=false;
            }
            else{
                if(!f[v][1])
                    f[n][0]=false;
            }
        }
    }
    return d[n][flag];
}
int main(){
    while(cin>>n&&n){
        string s,boss;
        for(int i=0;i<=n;i++)
            sons[i].clear();
        dict.clear();
        cnt=0;
        cin>>boss;
        ID(boss);
        for(int i=1;i<n;i++){
            cin>>s>>boss;
            sons[ID(boss)].push_back(ID(s));
        }
        int ans;
        ans=max(dp(0,0),dp(0,1));
        cout<<ans;
        bool unique=false;
        if(d[0][0]>d[0][1]&&f[0][0])
            unique=true;
        if(d[0][0]<d[0][1]&&f[0][1])
            unique=true;
        if(unique){
            cout<<" Yes"<<endl;
        }
        else{
            cout<<" No"<<endl;
        }
    }
    return 0;
} 

结果

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值