UVa 1220 - Party at Hali-Bula(树形DP)

链接:

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3661

 

题意:

公司里有n(n≤200)个人形成一个树状结构,即除了老板之外每个员工都有唯一的直属上司。
要求选尽量多的人,但不能同时选择一个人和他的直属上司。问:最多能选多少人,以及在人数最多的前提下方案是否唯一。

 

分析:

本题几乎就是树的最大独立集问题,不过多了一个要求:判断唯一性。
一、设d(u,0)和f(u,0)表示以u为根的子树中,不选u点能得到的最大人数以及方案唯一性(f(u,0)=1表示唯一,0表示不唯一)。
二、设d(u,1)和f(u,1)表示以u为根的子树中,选u点能得到的最大人数以及方案唯一性。相应地,状态转移方程也有两套。
三、d(u,1)的计算:因为选了u,所以u的子结点都不能选,故d(u,1) = sum{d(v,0) | v是u的子结点}。当所有f(v,0)=1时f(u,1)=1。
四、d(u,0)的计算:因为u没有选,所以每个子结点v可选可不选,即d(u,0) = sum{ max(d(v,0), d(v,1)) }。
什么情况下方案是唯一的呢?首先,如果某个d(v,0)和d(v,1)相等,则不唯一;
其次,如果max取到的那个值对应的f=0,方案也不唯一(如d(v,0) > d(v,1) 且f(v,0)=0,则f(u,0)=0)。

 

代码:

 1 #include <cstdio>
 2 #include <map>
 3 #include <string>
 4 #include <vector>
 5 using namespace std;
 6 
 7 const int UP = 200 + 5;
 8 int cid, d[UP][2]; // d数组表示以f为根的子树中,选或不选f点能得到的最大人数
 9 bool u[UP][2]; // u数组表示以f为根的子树中,选或不选f点的方案唯一性
10 map<string, int> M;
11 vector<int> son[UP];
12 
13 int id(char* s){
14     if(M.count(s)) return M[s];
15     return M[s] = cid++;
16 }
17 
18 int dp(int f, int p){
19     d[f][p] = p;
20     u[f][p] = true;
21     for(int i = 0; i < son[f].size(); i++){
22         int b = son[f][i];
23         if(p == 1){
24             d[f][1] += dp(b, 0);
25             if(!u[b][0]) u[f][1] = false;
26         }
27         else{
28             d[f][0] += max(dp(b, 0), dp(b, 1));
29             if(d[b][0] == d[b][1]) u[f][0] = false;
30             else if(d[b][0] > d[b][1] && !u[b][0]) u[f][0] = false;
31             else if(d[b][1] > d[b][0] && !u[b][1]) u[f][0] = false;
32         }
33     }
34     return d[f][p];
35 }
36 
37 int main(){
38     int n;
39     char f[999], b[999];
40     while(scanf("%d", &n) && n){
41         cid = 0;  M.clear();
42         for(int i = 0; i < n; i++) son[i].clear();
43 
44         scanf("%s", f);  id(f);
45         for(int i = 1; i < n; i++){
46             scanf("%s%s", b, f);
47             son[id(f)].push_back(id(b));
48         }
49 
50         printf("%d ", max(dp(0, 0), dp(0, 1)));
51         bool ok = (d[0][0]>d[0][1]&&u[0][0]) || (d[0][1]>d[0][0]&&u[0][1]);
52         printf("%s\n", ok ? "Yes" : "No");
53     }
54     return 0;
55 }

 

posted @ 2018-03-22 22:55  Ctfes  阅读(186)  评论(0编辑  收藏  举报