UVa传送门
洛谷RemoteJudge传送门
题目大意:公司里面有n(n ≤ 200)个 人形成一个树结构,即除了老板之外的每个员工都有且仅有一个直属上司。要求选尽量多的人,但不能同时选择一个人和他的直属上司。
问:1.最多可以选多少人。
2.方案是否唯一。
这道题就是典型的树的最大独立集问题。
树的最大独立集问题
先讲一种解决方法。我们用
表示以
为根节点的子树的最大独立集大小。需要注意的是,在解决问题前,需要将无根树转换成有根树,这样上述的状态才有意义。
结点i一共有两种决策,选和不选,对应地,就有两个方程
如果不选
,
如果选
,
接下来对两个答案取个
就可以了。
代码实现的时候要注意一点,就是不能从
去枚举
的儿子结点和孙子结点,而要从
的儿子节点和孙子结点去找
。换句话说,就是计算出一个d(i)之后,去更新
和
。
看懂了上面一种,我们再讲一种更加实用的办法。
我们用
表示以
为根的子树中,不选
点所能取到的最多的顶点数目。相应地,我们用
表示以
为根的子树中,选
点能取到的最多节点数目。
由于有两种状态,所以有两个转移方程。
其中
是
的子节点。
有了刚才的模型,我们再来看这道题。
可以发现这道题几乎就是树的最大独立集问题,但还添加了一个方案是否唯一的询问。
我们采取第二种解决的方案,并令
表示取到
方案的唯一性,相应地,
表示取到
方案的唯一性。
我们用
表示当前的结点,
是
的子节点。
当某个
与
相等或
操作取到的那个对应值的
时取
,否则为
则是对所有
取
,换言之,当且仅当所有的
时
才是
.
就保存在
和
中,
就是那个可以任意选定的根。
#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;
}