【解题报告】小胖守皇宫

【树形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 ] ) f[i,1]=k[i]+ \sum_{j=1}^s min(f[son[j],1],f[son[j],2],f[son[j],3]) f[i,1]=k[i]+j=1smin(f[son[j],1],f[son[j],2],f[son[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 ] ) f[i,2]=difference+ \sum_{j=1}^s min(f[son[j],1],f[son[j],2]) f[i,2]=difference+j=1smin(f[son[j],1],f[son[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 ] ) f[i,3]=\sum_{j=1}^s min(f[son[j],1],f[son[j],2]) f[i,3]=j=1smin(f[son[j],1],f[son[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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值