poj1947

树上背包?

问最少断掉多少条边可以形成节点数为k的块

设f[i,j]表示以节点i为根,形成一个节点数为k的块要断多少条边

则有:f[x,j]:=min(f[x,j],f[x,j-k]+f[y,k]-2) y是x的孩子

为什么要减2,现在装入以y为节点的子树,x和y之间的边,这条边自然不用断了,但在计算f[y,k]和f[x,1]的时候,这条边被断了两次

 1 const max=10006;
 2 var f,son:array[0..200,0..200] of longint;
 3     fa,s,t:array[0..200] of longint;
 4     j,x,y,i,n,m,root,ans:longint;
 5 
 6 procedure treedp(x:longint);
 7   var i,y,j,k:longint;
 8   begin
 9     t[x]:=1;
10     for i:=1 to s[x] do
11     begin
12       treedp(son[x,i]);
13       t[x]:=t[x]+t[son[x,i]];   //先统计子树规模
14     end;
15     f[x,1]:=s[x];
16     if x<>root then inc(f[x,1]);   //细节 注意要断他父亲和它之间的边
17     for i:=1 to s[x] do
18     begin
19       y:=son[x,i];
20       for j:=t[x] downto 2 do   //注意背包的倒推,每个子树显然只能用一次,相当于01背包
21         for k:=1 to t[y] do
22           if j-k>=1 then f[x,j]:=min(f[x,j],f[x,j-k]+f[y,k]-2)
23           else break;
24     end;
25 
26   end;
27 
28 begin
29   readln(n,m);
30   for i:=1 to n-1 do
31   begin
32     readln(x,y);
33     s[x]:=s[x]+1;   //题目中给定了父子关系
34     son[x,s[x]]:=y;
35     fa[y]:=x;
36   end;
37   for i:=1 to n do
38     for j:=1 to n do
39       f[i,j]:=max;
40   root:=1;
41   while fa[root]<>0 do inc(root);
42   treedp(root);
43   ans:=max;
44   for i:=1 to n do           
45     ans:=min(ans,f[i,m]);
46   writeln(ans);
47 end.
View Code

很好的treedp,一般来说,treedp都是先dfs再回来做dp……

总的复杂度O(n^3);题目其实不难,多想想就好

原文地址:https://www.cnblogs.com/phile/p/4473277.html