[树状dp] Party at Hali-Bula UVA1220

题意

有人要邀请一个等级森严的公司里的一些员工参加聚会,但如果邀请了某个员工的上司,那么这个人就不会参加聚会。求能参加聚会的最大人数和邀请方案是否是唯一的。

题解

看到本题第一想法是套最大独立集,由于还要求邀请方案是否唯一,就采用类似刷表的标记。

虽然AC了,但代码感觉很丑。紫书上给出了领一种动态规划的方法。感觉紫书上的方法更好一些。

动态规划做到现在也有点能区分出各种动态规划的区别了,哈


9月25日更新

先贴出基本的最大独立集的状态转移方程和紫书上本题的转移方程

基本的最大独立集:d(i)=max \{1+\sum _{j\in gs(i)}d(j), \sum_{j\in s(i)}d(j)\}

本题的状态转移方程:\begin{Bmatrix} d(u,1) = sum\{ d(v,0)|v\in s(u)\} \\ d(u,0) = sum\{ max(d(v,0),d(v,1))\} \end{matrix}

首先,本题的转移方程有一个特点。它的状态是基本最大独立集的状态的细分。可以看到本题的最终答案是在d(u,1 )d(u,0 )中取最大值,而这也对应着第一个状态转移方程的前半部分与后半部分。从直观上看,状态简单其转移方程也简单,状态复杂要考虑的情况也就越多,其转移方程就相对复杂。

其次,本题的状态转移方程如此设计有以下两个优点:

1. 本题的状态转移方程只涉及其子节点。通过增加一个状态变量,显式体现了选该点还是不选该点的决策,从而避免了对其孙子的访问。这就为记忆化搜索和递推的实现提供了方便。

2.  这点比较具体,这种状态转移方程方便确定方案是否唯一。在判断方案是否唯一时,如果选该点和不选该点的节点数相同,那么该点为根的子树方案不唯一。这点两种状态转移方程都很好判断。而问题在于,当该点的父节点(或祖父节点)选择该节点时,其方案也是不唯一的。这就要求我们要知道每一步选择了那个决策,并且这个决策是否是一个多方案决策。由于我用了第一种状态转移方程,因此只能另开数组刷是否唯一的状态。若采用紫书上的状态转移方程,则开一个同状态数组跟着u转移即可。

直观来看,这不就是在动态规划中同时维护两个值(类似[线性dp] Mathematical Curse)。同时维护的多个值得转移要保持同步。实现的方式也很多,比如另开数组,维护结构体([01背包] Jin Ge Jin Qu hao UVa12563),增加状态,当然这些本质都相同。

AC代码

#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>
#include <functional>
#include <algorithm>
#include <unordered_map>
#include <string>
#include <iostream>
using namespace std;

typedef long long ll;

const int maxn = 200 + 10;
const int INF = 0x3f3f3f3f;

vector<int> G[maxn];
int boss[maxn];
int gs[maxn], s[maxn];
bool flag[maxn][2],f;
int n;

int dp(int u){
    if(G[u].empty()){
        if(u)s[boss[u]] ++;
        if(u && boss[u])gs[boss[boss[u]]]++;
        return 1;
    }
    for(int i = 0; i<G[u].size(); i++){
        dp(G[u][i]);
    }
    int du = max(1+gs[u], s[u]);
    if(u)s[boss[u]] += du;
    if(u && boss[u]) gs[boss[boss[u]]] += du;
    if(s[u] == gs[u]+1){
        if(u==0) f = true;
        if(u)flag[boss[u]][0] = true;
        if(u&&boss[u]) flag[boss[boss[u]]][1] = true;
    }
    if(du == gs[u]+1 && flag[u][1]){
        if(u==0) f = true;
        if(u)flag[boss[u]][0] = true;
        if(u&&boss[u]) flag[boss[boss[u]]][1] = true;
    }
    else if(du == s[u] && flag[u][0]){
        if(u==0) f = true;
        if(u)flag[boss[u]][0] = true;
        if(u&&boss[u]) flag[boss[boss[u]]][1] = true;
    }

    return du;
}


int main(){
    ios::sync_with_stdio(false);
    while(cin>>n && n){
        for(int i = 0; i<=n; i++) G[i].clear();
        memset(gs, 0, sizeof(gs));
        memset(s, 0, sizeof(s));
        memset(flag, false, sizeof(flag));
        f = false;
        unordered_map<string, int> mp;
        string bb,em;
        int ncnt = 0;
        cin >> bb;
        mp[bb] = ncnt++;
        int ne, nb;
        for(int i = 1; i<n; i++){
            cin >> em >> bb;
            auto p = mp.find(em);
            if(p == mp.end()){
                ne = ncnt;
                mp[em] = ncnt++;
            }
            else ne = p->second;
            p = mp.find(bb);
            if( p == mp.end()){
                nb = ncnt;
                mp[bb] = ncnt++;
            }
            else nb = p->second;
            G[nb].push_back(ne);
            boss[ne] = nb;
        }
        cout<<dp(0)<<" ";
        if(f) cout << "No"<< endl;
        else cout << "Yes" << endl;

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值