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