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

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


你可能感兴趣的:(SSL 1606 选课 树转二叉树+树形dp)