SSL 1608 皇宫看守 树形dp

题意:有一棵树,每个节点都有一个放置守卫的费用。若在某个节点放置守卫则与该节点相连的所有节点都能被看到。问能看到所有节点的最小费用是多少。


分析:树形dp

f[i]表示节点i防守卫的最小费用。

g[i]表示节点i不放守卫且被看到的最小费用。

h[i]表示节点i不被看到但所有子节点都被看到的最小费用。

f[i]=sum(min(f[soni],g[soni],h[soni]))

g[i]=sum(min(f[soni],g[soni]))若没有一个子节点是放置守卫则让增量最小的子节点防止守卫。

h[i]=sum(g[soni])

最后输出min(g[root],f[toot])


代码:

var
  n,x,y,z,i,j,root:longint;
  v:array[1..1500] of boolean;
  g,h,f,d:array[1..1500] of longint;
  a:array[1..1500,0..1500] of longint; 

function min(x,y:longint):longint;
begin
  if x<y then exit(x)
         else exit(y);
end;

procedure dp(x:longint);
var
  i,w:longint;
  flag:boolean;
begin
  flag:=true;
  f[x]:=d[x];
  for i:=1 to a[x,0] do
  begin
    dp(a[x,i]);
    f[x]:=f[x]+min(f[a[x,i]],min(g[a[x,i]],h[a[x,i]]));
    if f[a[x,i]]<=g[a[x,i]]
      then begin
             g[x]:=g[x]+f[a[x,i]];
             flag:=false;
           end
      else g[x]:=g[x]+g[a[x,i]];
    h[x]:=h[x]+g[a[x,i]];
  end;
  if flag then
  begin
    w:=maxlongint div 1500;
    for i:=1 to a[x,0] do
      w:=min(w,f[a[x,i]]-g[a[x,i]]);
    g[x]:=g[x]+w;
  end;
end;

begin
  readln(n);
  fillchar(v,sizeof(v),true);
  for i:=1 to n do
  begin
    read(x,y);
    d[x]:=y;
    read(y);
    for j:=1 to y do
    begin
      read(z);
      inc(a[x,0]);
      a[x,a[x,0]]:=z;
      v[z]:=false;
    end;
  end;
  for i:=1 to n do
    if v[i] then
    begin
      root:=i;
      break;
    end;
  dp(root);
  writeln(min(f[root],g[root]));
end.


你可能感兴趣的:(SSL 1608 皇宫看守 树形dp)