SSL 1606 选课 树转二叉树+树形dp

来源:互联网 发布:mac persistence 编辑:程序博客网 时间:2024/06/11 10:56

题意:有n门课,每门课都有可能有一门先修课,也就是必须选了先修课才能选这门课。选择每门课都可以获得一定的学分。求在只能选m门课的情况下最高能获得多少学分。


分析:这是一棵树所以很容易想到树形dp。

但又考虑到这是一棵多叉树所以要先转换成二叉树。

方法:

(1)加线:在各兄弟结点之间用虚线相链。可理解为每个结点的兄弟指针指向它的一个兄弟。
(2)抹线:对每个结点仅保留它与其最左一个孩子的连线,抹去该结点与其它孩子之间的连线。可理解为每个结点仅有一个孩子指针让它指向自己的长子。
(3)旋转:把虚线改为实线从水平方向向下旋转45℃,成右斜下方向。原树中实线成左斜下方向。这样就树的形状成呈现出一棵二叉树

然后就可以进行树形dp了。

f[i,j]表示节点i为根的树中选j门课最多能获得多少学分。

f[i,j]=max(f[rson,j],max(f[lson,k]+f[rson,j-k-1]+d[i]))


代码:

var  n,m,i,x,y:longint;  a,f:array[-1..1000,0..1000] of longint;  l,r,d,s:array[-1..1000] of longint;procedure dfs(x:longint);var  i:longint;begin  for i:=1 to a[x,0]-1 do    r[a[x,i]]:=a[x,i+1];  if a[x,0]>0 then l[x]:=a[x,1];  for i:=1 to a[x,0] do    dfs(a[x,i]);end;procedure dfs1(x:longint);begin  s[x]:=1;  if l[x]>0 then  begin    dfs1(l[x]);    s[x]:=s[x]+s[l[x]];  end;  if r[x]>0 then  begin    dfs1(r[x]);    s[x]:=s[x]+s[r[x]];  end;end;function max(x,y:longint):longint;begin  if x>y then exit(x)         else exit(y);end;function min(x,y:longint):longint;begin  if x<y then exit(x)         else exit(y);end;procedure dp(x:longint);var  i,j:longint;begin  if x=-1 then exit;  dp(l[x]);  dp(r[x]);  for i:=1 to min(s[x],m+1) do  begin    f[x,i]:=f[r[x],i];    for j:=0 to i-1 do      f[x,i]:=max(f[x,i],f[l[x],j]+f[r[x],i-j-1]+d[x]);  end;end;begin  readln(n,m);  for i:=1 to n do  begin    readln(x,y);    inc(a[x,0]);    a[x,a[x,0]]:=i;    d[i]:=y;  end;  for i:=0 to n do  begin    l[i]:=-1;    r[i]:=-1;  end;  dfs(0);  dfs1(0);  dp(0);  writeln(f[0,m+1]);end.


0 0