【解题报告】小胖守皇宫

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TengWan_Alunl/article/details/74853144

【树形DP】小胖守皇宫

题目来源:Vijos1144。

根据题意,此题要求求最优值,并且按照从叶向根这个决策顺序来看,后面的决策不会影响到前面的决策,满足无后效性。再来观察这一道题目的最优子结构,我们可以发现对于任意一个中间节点i来说,当i的所有子节点均取得最优值时,i也取得最优值。有了这三个要素(最优值、无后效性、最优子结构),因此姑且套用动态规划来一做。


动态规划的核心是状态转移方程。正所谓状态转移方程,我们就需要先知道有哪一些状态。由题意得知,所有的宫殿均要被看守到,于是对于任意一个宫殿i来说,必处于以下三种状态中的任意一种:

 1. 被自己看守
 2. 被儿子看守(叶节点无此状态)
 3. 被父亲看守(根节点无此状态)

我们设f[i,x]为以i为根的子树(包括i),且i处于第x个状态(按上文所述顺序)时的最小经费开销。
下面,我们来探求转移方程。

  1. 宫殿i被自己看守:
    因为i处有守卫,所以i的所有直接子节点放不放守卫都无伤大雅,故将所有子节点的最小开销(即是从三个状态下对应的三种开销中取得的最小值)之和,再加上在i处放置守卫的开销作为宫殿i的最小开销。
    得:
    f [ i , 1 ] = k [ i ] + j = 1 s m i n ( f [ s o n [ j ] , 1 ] , f [ s o n [ j ] , 2 ] , f [ s o n [ j ] , 3 ] )
    (s为i的儿子节点个数)
  2. 宫殿i被儿子看守:
    先得出两个显然的结论:因为i被子节点看守,所以i的所有子节点中需要至少有一个子节点被放置了守卫。并且任意子节点j不可能被父亲看守,因此j的最优值从f[j,1]与f[j,2]中取最小值得到。不幸的是,如果j均满足f[j,1]>f[j,2],那么就没有j会被放置守卫,这与上几行中加粗的结论是冲突的。在这种情况下我们就必须要强行在一个子节点上放置守卫,既然如此,那我们当然要选择一个放置代价(f[j,1]-f[j,2])最小的j放置守卫来解决这一问题。
    得:
    f [ i , 2 ] = d i f f e r e n c e + j = 1 s m i n ( f [ s o n [ j ] , 1 ] , f [ s o n [ j ] , 2 ] )
    (difference即为放置的最小代价,单词意为差值,即将放置代价最小的子节点x从被儿子看守的状态转为到被自己看守的状态所需要多花的钱)
  3. 宫殿i被父亲看守:
    因为i被父亲看守了,所以i上必定无守卫,则i的子节点j要么由自己看守,要么由j自己的子节点看守。
    得:
    f [ i , 3 ] = j = 1 s m i n ( f [ s o n [ j ] , 1 ] , f [ s o n [ j ] , 2 ] )

AC代码:(实际上我使用的方法叫记忆化搜索,但其原理和DP是一样的,只是写起来有点不同。)

program vijos1144;
var n,i,j,a,root:integer;
    tree:array[1..1500,0..1500]of integer;
    k:array[1..1500]of longint;
    mark:array[1..1500]of boolean;
    f:array[1..1500,1..3]of qword;//1 self,2 child,3 father.
function min(a,b:qword):qword;
begin
    if(a<b)then  exit(a);
    exit(b);
end;
procedure dp(v:integer);
var i,diff:longint;//diff means difference.
begin
    if(tree[v,0]=0)then exit;
    diff:=maxlongint;
    for i:=1 to tree[v,0] do begin
        dp(tree[v,i]);
        inc( f[v,1], min( min(f[tree[v,i],1],f[tree[v,i],2]), f[tree[v,i],3]) );
        inc( f[v,2], min( f[tree[v,i],1], f[tree[v,i],2]) );
        diff:=min(diff, f[tree[v,i],1]-min(f[tree[v,i],1],f[tree[v,i],2]) );
        inc( f[v,3], min(f[tree[v,i],1], f[tree[v,i],2]));
    end;
    inc(f[v,1],k[v]);
    inc(f[v,2],diff);
    for i:=1 to 3 do
      if f[v,i]>maxlongint then f[v,i]:=maxlongint;

end;
begin
    readln(n);
    for i:=1 to n do begin
        read(a); read(k[a],tree[a,0]);
        for j:=1 to tree[a,0] do begin read(tree[a,j]); mark[tree[a,j]]:=true; end;
    end;
    for i:=1 to n do
        if(not mark[i])then begin root:=i; break; end;
    for i:=1 to n do
        if(tree[i,0]=0)then begin f[i,2]:=maxlongint; f[i,1]:=k[i]; end;

    dp(root);

    writeln(min(f[root,1], f[root,2]));
end.

猜你喜欢

转载自blog.csdn.net/TengWan_Alunl/article/details/74853144