UVA 1220 Party at Hali-Bula (树形dp--树的最大独立集)

大体题意:

给你n 个人的上司和下属的关系,让你挑尽可能多的人来参加聚会,参加聚会的条件是 任意两个人不能具有直接的上司下属的关系,求最大人数?

思路:

树形dp  -- 树的最大独立集问题:

只不过多了一个条件,问是否唯一:

解法很容易想到了:

令dp[i][0]表示 第i 个人 如果不选择的话,最大人数,  令f[i][0] 为第i 个人不选择的话,解是否唯一。

同样的  令dp[i][1]为第i 个人选择的话,最大的人数,令f[i][1] 表示第i 个人选择的话,解是否唯一。

那么直接建图从1号老板开始dfs,在回溯的过程中进行状态转移。

转移dp[i][0]时,因为第i个人不选择,那么他的儿子结点 都可以要或者不要    max{dp[v][0],dp[v][1]}在说说f的转移

如果dp[v][0] == dp[v][1] 解肯定不唯一, 否则谁大,就判断谁的f!两边取个交集即可!

同样的,转移dp[i][1]时,因为第i 个人选择,那么他的儿子结点都不要,只能取dp[v][0],只判断  f[v][0]就好了!

详细见代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
const int maxn = 200 + 7;
map<string,int>mp;
vector<int>g[maxn];
int id, n;
int dp[maxn][2],f[maxn][2];
char s[maxn],son[maxn];
int ID(string s){
    if (!mp.count(s)){
        return mp[s] = ++id;
    }
    return mp[s];
}
void init(){
    id = 0;
    for (int i = 0; i < maxn; ++i) g[i].clear();
    mp.clear();
}
void dfs(int k){
    int len = g[k].size();
    if (!len){
        dp[k][0] = 0; dp[k][1] = 1;
        f[k][0] = 1; f[k][1] = 1;
        return ;
    }
    dp[k][0] = 0;
    dp[k][1] = 1;
    f[k][0] = f[k][1] = 1;
    for (int i = 0; i < len; ++i){
        int v = g[k][i];
        dfs(v);
        if (dp[v][0] == dp[v][1]){
            dp[k][0] += dp[v][0];
            f[k][0] = 0;
        }
        else if (dp[v][0] > dp[v][1]){
            dp[k][0] += dp[v][0];
            f[k][0] = (f[k][0] && f[v][0]);
        }
        else {
            dp[k][0] += dp[v][1];
            f[k][0] = (f[k][0] && f[v][1]);
        }

        dp[k][1] += dp[v][0];
        f[k][1] = (f[k][1] && f[v][0]);
    }
}
int main(){

    while(~scanf("%d",&n) && n){
        scanf("%s",s);
        init();
        ID(s);
        for (int i= 0 ; i < n-1; ++i){
            scanf("%s%s",s,son);
            g[ID(son)].push_back(ID(s));
        }
        dfs(1);
        if (dp[1][1] > dp[1][0]) {
            printf("%d ",dp[1][1]);
            if (f[1][1])puts("Yes");
            else puts("No");
        }
        else if (dp[1][1] < dp[1][0]){
            printf("%d ",dp[1][0]);
            if (f[1][0])puts("Yes");
            else puts("No");
        }
        else {
            printf("%d No\n",dp[1][1]);
        }
    }


    return 0;
}






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值