poj 1947 Rebuilding Roads 树形DP

题意:给你一棵节点为n的树,问至少砍几刀可以孤立出一棵节点为m的子树。

分析:我的树形DP还不是很熟,所以也是看了别人的题解才勉强写了出来。

http://www.cnblogs.com/yu-chao/archive/2011/07/18/2109730.html

就是这个网址大家如果看不懂我的就去看这个吧。

首先这题一看就是树形DP(就不告诉你为什么)。用f[x,j]表示以节点x为根要留j个节点(包括x)需要砍多少刀。首先f[x,1]=x的儿子数量。对于x的一个儿子y,若要在以y为根的树上保留k个节点,则f[x,j]=min(f[y,k]+f[x,j-k])-1,至于为什么要减1,是因为在之前的状态都是从f[x,1]推过来的,然后f[x,1]默认的是所有子树都被砍了,所以在之前的状态中以y为根的树都是被砍掉的,所以现在要接回去,也就是-1(好绕)。

搞定之后还有一件事要注意:最后剩下的树不一定是以原本的根节点为根节点,所以还要把所有节点都找一遍。

代码:

var
  n,p,i,x,y,root,ans:longint;
  s:array[1..150] of longint;
  a:array[0..150,0..150] of longint;
  f:array[1..150,1..150] of longint;
  v:array[1..150] of boolean;

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

procedure dfs(x:longint);
var
  i,j,k:longint;
begin
  for i:=1 to a[x,0] do
    dfs(a[x,i]);
  f[x,1]:=a[x,0];
  s[x]:=1;
  for i:=1 to a[x,0] do
    s[x]:=s[x]+s[a[x,i]];
  for i:=1 to a[x,0] do
    for j:=s[x] downto 2 do
      for k:=1 to min(j-1,s[a[x,i]]) do
        if f[a[x,i],k]+f[x,j-k]-1<f[x,j] then
          f[x,j]:=f[a[x,i],k]+f[x,j-k]-1;
end;

begin
  readln(n,p);
  fillchar(f,sizeof(f),$7f div 10);
  fillchar(v,sizeof(v),true);
  fillchar(a,sizeof(a),0);
  for i:=1 to n-1 do
  begin
    readln(x,y);
    inc(a[x,0]);
    a[x,a[x,0]]:=y;
    v[y]:=false;
  end;
  for i:=1 to n do
    if v[i] then
    begin
      root:=i;
      break;
    end;
  dfs(root);
  ans:=f[root,p];
  for i:=1 to n do
    if f[i,p]+1<ans then ans:=f[i,p]+1;
  writeln(ans);
end.


你可能感兴趣的:(poj 1947 Rebuilding Roads 树形DP)