链接:
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 }