题意:有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.