题意:公司有 n 个人形成一个树形结构,除了老板都有唯一的一个直系上司,要求选尽量多的人,但不能同时选一人上和他的直系上司,问最多能选多少人,并且是不是唯一的方案。
析:这个题几乎就是树的最大的独立集问题,只不过多一个判断唯一性而已。用两个数组,一个用来记录人数,一个用来判断唯一性。
d[u][0],表示以 u 为根的子树中,不选 u 点能够得到最大人数,那么d[u][1]就是选 u 点能达到最大人数。
f[u][0]类似,表示以 u 为根的子树中,不选 u 点是否唯一,那么f[u][1]就是选 u 点是否唯一。
对于d[u][1]的计算,因为选择了 u,那么 u 的子结点都不能选,所以就是 d[u][1] = sum(d[v][0], v是子结点),当f[v][0] 是不唯一时,f[u][1] 也不唯一。
对于d[u][0]的计算,因为没有选择了 u,那么它的子结点可以选也可以不选,也就是选最大的,即d[u][0] = sum(max(d[v][0], d[v][1])),那么这个唯一性怎么判断呢?和上面差不多,
就多了一个,如果d[v][0] == d[v][1],那么这个就是不唯一的了,其他的和上面一样。剩下的就简单了,用DFS即可。
代码如下:
#include <iostream> #include <cstdio> #include <map> #include <cstring> #include <vector> using namespace std; const int maxn = 200 + 5; vector<int> G[maxn]; bool f[maxn][2]; int d[maxn][2], n, cnt;//d[u][0] 不选 ,d[u][0] 选 map<string, int> id; void init(){//初始化 for(int i = 1; i <= n; ++i) G[i].clear(); memset(f, false, sizeof(f)); memset(d, 0, sizeof(d)); cnt = 0; id.clear(); } int getid(const string &s){//获得id if(id.count(s)) return id[s]; return id[s] = ++cnt; } void dfs(int u){ if(!G[u].size()){//最下端 d[u][0] = 0; d[u][1] = 1; return ; } for(int i = 0; i < G[u].size(); ++i){ int son = G[u][i]; dfs(son); d[u][1] += d[son][0];//d[u][1]的计算 if(f[son][0]) f[u][1] = true;//判断唯一性 if(d[son][0] > d[son][1]){//d[u][0]的计算 d[u][0] += d[son][0]; if(f[son][0]) f[u][0] = true; } else if(d[son][0] == d[son][1]){//相等,那就不唯一 d[u][0] += d[son][0]; f[u][0] = true; } else { d[u][0] += d[son][1]; if(f[son][1]) f[u][0] = true; } } ++d[u][1];//别忘了加1 } int main(){ while(scanf("%d", &n) == 1 && n){ init(); string s1, s2; cin >> s1; getid(s1); for(int i = 0; i < n-1; ++i){ cin >> s1 >> s2; G[getid(s2)].push_back(getid(s1)); } dfs(1); if(d[1][0] == d[1][1]) printf("%d No ", d[1][0]);//判断谁在数更大 else if(d[1][0] > d[1][1]) printf("%d %s ", d[1][0], f[1][0] ? "No" : "Yes"); else printf("%d %s ", d[1][1], f[1][1] ? "No" : "Yes"); } return 0; }