【题解】UVa 1220 Party at Hali-Bula

UVa传送门
洛谷RemoteJudge传送门
题目大意:公司里面有n(n ≤ 200)个 人形成一个树结构,即除了老板之外的每个员工都有且仅有一个直属上司。要求选尽量多的人,但不能同时选择一个人和他的直属上司。
问:1.最多可以选多少人。
2.方案是否唯一。
这道题就是典型的树的最大独立集问题

树的最大独立集问题

先讲一种解决方法。我们用 d ( i ) 表示以 i 为根节点的子树的最大独立集大小。需要注意的是,在解决问题前,需要将无根树转换成有根树,这样上述的状态才有意义。
结点i一共有两种决策,选和不选,对应地,就有两个方程
如果不选 i d ( i ) = j s ( i ) d ( j )
如果选 i d ( i ) = 1 + j g s ( i ) d ( j )
接下来对两个答案取个 m a x 就可以了。
代码实现的时候要注意一点,就是不能从 i 去枚举 i 的儿子结点和孙子结点,而要从 i 的儿子节点和孙子结点去找 i 。换句话说,就是计算出一个d(i)之后,去更新 d ( f a t h e r ( i ) ) d ( f a t h e r ( f a t h e r ( i ) ) )

看懂了上面一种,我们再讲一种更加实用的办法
我们用 d ( i , 0 ) 表示以 i 为根的子树中,不选 i 点所能取到的最多的顶点数目。相应地,我们用 d ( i , 1 ) 表示以 i 为根的子树中,选 i 点能取到的最多节点数目。
由于有两种状态,所以有两个转移方程。
d ( u , 0 ) = m a x ( d ( v , 1 ) , d ( v , 0 ) )
d ( u , 1 ) = d ( v , 0 ) + 1
其中 v u 的子节点。

有了刚才的模型,我们再来看这道题。
可以发现这道题几乎就是树的最大独立集问题,但还添加了一个方案是否唯一的询问。
我们采取第二种解决的方案,并令 f ( i , 0 ) 表示取到 d ( i , 0 ) 方案的唯一性,相应地, f ( i , 1 ) 表示取到 d ( i , 1 ) 方案的唯一性。
我们用 u 表示当前的结点, v u 的子节点。
f ( u , 0 ) 当某个 d ( v , 0 ) d ( v , 1 ) 相等或 m a x 操作取到的那个对应值的 f == 0 时取 f a l s e ,否则为 t r u e
f ( u , 1 ) 则是对所有 f ( v , 0 ) ,换言之,当且仅当所有的 f ( v , 0 ) = 1 f ( u , 1 ) 才是 1 .
A n s 就保存在 m a x ( d ( r , 0 ) , d ( r , 1 ) ) m a x ( f ( r , 0 ) , f ( r , 1 ) ) 中, r 就是那个可以任意选定的根。

C o d e

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector> 
#include<map>
#define MAXN 500
struct EdgeType {
    int to, next;
};
std::map< std::string, int > M;
std::vector< EdgeType > edge;
int head[MAXN], d[MAXN][2], num;
bool f[MAXN][2];
inline int ID(const std::string& rhs) {
    if (!M.count(rhs)) M[rhs] = ++num;
    return M[rhs];
}
inline void AddEdge(int from, int to) {
    edge.push_back((EdgeType){to, head[from]});
    head[from] = edge.size() - 1;
}
void solve(int u) {
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        solve(v);
        int temp = d[v][0] > d[v][1] ? 0 : 1;
        d[u][0] += d[v][temp];
        f[u][0] = f[u][0] & f[v][temp] & (d[v][0] != d[v][1]);
        d[u][1] += d[v][0];
        f[u][1] &= f[v][0];
    }
}
int main() {
    int n;
    std::string employee, boss;
    while (std::cin >> n >> boss) {
        M.clear();
        num = 0;        
        memset(head, -1, sizeof head);
        memset(f, true, sizeof f);
        for (int i = 1; i <= n; i++) 
            d[i][1] = 1, d[i][0] = 0;
        edge.clear();
        ID(boss);
        for (int i = 1; i < n; i++) {
            std::cin >> employee >> boss;
            AddEdge(ID(boss), ID(employee)); 
        }
        solve(1);
        int temp = d[1][0] > d[1][1] ? 0 : 1;
        printf("%d ", d[1][temp]);
        if (f[1][temp] && d[1][0] != d[1][1]) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/diogenes_/article/details/80556493