UVa #1220 Party at Hali-Bula (例题9-13)

86 篇文章 0 订阅

新年第一题,一道树上的动态规划,树的最大独立集问题。


书中介绍最大独立集时提到了刷表法,却说有一种更实用的方法将在例题中介绍,应该说的就是这题。


按照这种方法的思路,我们不必枚举所有儿子和所有孙子,只需要加一个维度来表示是否选中当前节点然后只枚举儿子。


对于已经选中的节点,在记忆化搜索的递归时,将它所有儿子的这个维度设为“不选中”即可。

对于不选中的节点,则对于每个儿子,分别求出选中/不选中的dp值,然后选取较大者即可。


对于这道题的特殊要求:唯一性,书里用 f=1 表示“唯一”,f=0 表示“不唯一”,其实不太方便。因为对于某一个节点,只有他的全部儿子节点都唯一时,他自己才唯一。所以不如把 f 的用法调转,这样只要碰到不唯一的儿子,这个节点就可以设置成“不唯一”了。



Run Time: 0.035s

#define UVa  "LT9-13.1220.cpp"		//Party at Hali-Bula
char fileIn[30] = UVa, fileOut[30] = UVa;

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<map>
#include<iostream>
#include<string>

using namespace std;

//Global Variables. Reset upon Each Case!
const int maxn = 200 + 10;
int n;
vector<int> S[maxn];
map<string, int> Names;
int d[maxn][2];
int dup[maxn][2];
/

int dp(int u, int f) {
    if(d[u][f]) return d[u][f];
    if(S[u].size() == 0) {      //leaf node.
        d[u][f] = f;
    }
    else {                      //internal node.
        if(f == 1) {        //node selected.
            d[u][f] = 1;
            for(int i = 0; i < S[u].size(); i ++) {
                d[u][f] += dp(S[u][i], 0);
                if(dup[S[u][i]][0]) dup[u][f] = 1;
            }
        }
        else {              //node not selected.
            d[u][f] = 0;
            for(int i = 0; i < S[u].size(); i ++) {
                int a = dp(S[u][i], 0), b = dp(S[u][i], 1), c = max(a,b);
                d[u][f] += c;
                if( a == b
                    || (a > b && dup[S[u][i]][0])
                    || (a < b && dup[S[u][i]][1])
                   ) dup[u][f] = 1;
            }
        }
    }
    return d[u][f];
}

void init() {
    for(int i = 0; i < maxn; i ++) S[i].clear();
    Names.clear();
    memset(d, 0, sizeof(d));
    memset(dup, 0, sizeof(dup));
    string tmp, tmpboss;
    int cnt = 0;
    cin>>tmp;
    Names[tmp] = cnt++;
    for(int i = 1; i < n; i ++) {
        cin>>tmp>>tmpboss;
        int a, b;
        if(!Names.count(tmp))
            a = Names[tmp] = cnt ++;
        else
            a = Names[tmp];

        if(!Names.count(tmpboss))
            b = Names[tmpboss] = cnt ++;
        else
            b = Names[tmpboss];
        S[b].push_back(a);
    }
}

int main() {
    while(scanf("%d", &n) && n) {
        init();
        int a = dp(0,0), b = dp(0,1);
        if(a == b)
            dup[0][0] = dup[0][1] = 1;
        if(a > b)
            printf("%d %s\n", a, (dup[0][0])?"No":"Yes");
        else
            printf("%d %s\n", b, (dup[0][1])?"No":"Yes");
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值