题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4126
$LRJ$紫书例题$9-26$
题目大意:
给定一颗树 有些边已经标好方向 现在要给其余的边标上方向 使得最长的有向链最短
$HIT:$ 题目额外给了一个结论 假设已经确定方向的边所能得到的最长链为$k$ 最后的最长链一定$k$ 或$k + 1$
不知道是自己太久没有写树形$DP$还是这题的确比较麻烦 花了好久才折腾出来
首先 利用题目给的结论 我们实际上只需要解决以下这个几乎等价的问题
判断所给的树是否可以构造出最长链不超过$k$的方案
如果是的 那么最长链就是$k$否则为$k + 1$
对于这个可行解问题 我们可以这样考虑
对于每颗子树的最长链 它要么经过这颗子树的根节点 要么在这个根节点的某个儿子所对应的子树中
因此只需递归地去检验每颗子树是否合法即可
我们可以建立三个数组 $f[x][y]$ $up[x]$ $down[x]$
$f[x][y]$代表 到达该根节点$x$的向上的最长链最小值为$y$时向下的最长链最小值为多少
$up[x]$ $down[x]$分别代表该根节点向上/下的最长链的最小值
然后由于数据范围比较小 所以可以直接用这种$O(n^2)$的方法轻松解决
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 210, E = N << 1; 4 int firste[N], nexte[E], v[E], w[E]; 5 int n, e, ans; 6 void build(int x, int y, int z) 7 { 8 nexte[++e] = firste[x]; 9 firste[x] = e; 10 v[e] = y; 11 w[e] = z; 12 } 13 void init(int uu) 14 { 15 int vv; 16 char ch; 17 n = 0; 18 e = 1; 19 memset(firste, 0, sizeof firste); 20 ans = 0; 21 do 22 { 23 n = max(n, uu); 24 while(scanf("%d%c", &vv, &ch), vv) 25 { 26 n = max(n, vv); 27 if(ch == 'd') 28 { 29 build(uu, vv, 1); 30 build(vv, uu, 0); 31 } 32 else if(ch == 'u') 33 { 34 build(uu, vv, 0); 35 build(vv, uu, 1); 36 } 37 else 38 { 39 build(uu, vv, 0); 40 build(vv, uu, 0); 41 } 42 } 43 }while(scanf("%d", &uu), uu); 44 } 45 void dfs0(int u, int fa, int dist) 46 { 47 ans = max(ans, dist); 48 for(int p = firste[u]; p; p = nexte[p]) 49 if(w[p] && v[p] != fa) 50 dfs0(v[p], u, dist + 1); 51 } 52 int f[N][N], up[N], down[N]; 53 bool dfs(int u, int fa) 54 { 55 for(int p = firste[u]; p; p = nexte[p]) 56 if(v[p] != fa) 57 { 58 if(!dfs(v[p], u)) 59 return 0; 60 if(w[p]) 61 { 62 for(int i = 0; i <= min(down[v[p]], ans); ++i) 63 f[u][i] = ans + 1; 64 } 65 else if(w[p ^ 1]) 66 { 67 for(int i = 0; i <= ans; ++i) 68 f[u][i] = max(f[u][i], up[v[p]] + 1); 69 } 70 else 71 { 72 for(int i = 0; i <= down[v[p]]; ++i) 73 f[u][i] = max(f[u][i], up[v[p]] + 1); 74 } 75 } 76 bool re = 0; 77 for(int i = 0; i <= ans; ++i) 78 if(f[u][i] + i <= ans) 79 { 80 re = 1; 81 down[u] = min(down[u], i); 82 up[u] = min(up[u], f[u][i]); 83 } 84 return re; 85 } 86 int main() 87 { 88 int tmp; 89 while(scanf("%d", &tmp), tmp) 90 { 91 init(tmp); 92 for(int i = 1; i <= n; ++i) 93 dfs0(i, i, 0); 94 memset(f, 0, sizeof f); 95 memset(down, 0x3f, sizeof down); 96 memset(up, 0x3f, sizeof up); 97 if(dfs(1, 1)) 98 printf("%d ", ans + 1); 99 else 100 printf("%d ", ans + 2); 101 } 102 return 0; 103 }