树形DP

嗯奉上今天的题解~
今天我们练习了树形DP(嗯小学生的文风还是改不了~
总体来说还是不错的。。将很多以前学过但是没写过的东西付诸于键盘上
这是很有好处的~
T1
1. 加分二叉树(btree)
【题目描述】
设一个 n 个节点的二叉树tree 的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n 为节点编
号。每个节点都有一个分数(均为正整数),记第j 个节点的分数为di,tree 及它的每个子树都有一
个加分,任一棵子树subtree(也包含tree 本身)的加分计算方法如下:
subtree 的左子树的加分× subtree 的右子树的加分+subtree 的根的分数
若某个子树为主,规定其加分为 1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;
(1)tree 的最高加分
(2)tree 的前序遍历


这个之前就做过了,严格意义上来讲不算是树形DP
设f[i,j]表示从中序遍历从I到J得二叉树的最大加分
可得 f[i,j]=max{f[i,k-1]*f[k+1,j]+v[k] (k in [i..j]);

然后就可以AC辣~

Code:

const shuru='btree.in';
      shuchu='btree.out';
var   a:array[1..100] of int64;
      f:array[0..100,0..100] of int64;
      i,j,k,n:longint;
function max(a,b:int64):int64;
begin
	if a>b then exit(a);
	exit(b);
end;
procedure init;
begin
	assign(input,shuru);
	assign(output,shuchu);
	reset(input);
	rewrite(output);
	readln(n);
	for i:=1 to n do
		begin
			read(a[i]);
			f[i,i]:=a[i];
			f[i,i-1]:=1;	
			f[i,i+1]:=1;
		end;
	close(input);
end;
procedure search(x,y:longint);
var i:longint;
begin
	if x>y then exit;
	if x=y then begin write(x,' '); exit; end;
	for i:=x to y do
		if f[x,y]=f[x,i-1]*f[i+1,y]+a[i] then begin
							write(i,' ');
							search(x,i-1);
							search(i+1,y);
							exit;
						      end;
end;
procedure main;
begin
	init;
	for i:=n downto 1 do
		for j:=i+1 to n do
			for k:=i to j do
				f[i,j]:=max(f[i,j],f[i,k-1]*f[k+1,j]+a[k]);
	writeln(f[1,n]);
	search(1,n);
	close(output);
end;
begin
	main;
end.

T2
2. 选课(course)
【题目描述】
大学里实行学分。每门课程都有一定的学分,学生只要选修了这门课并考核通过就能获得相应的
学分。学生最后的学分是他选修的各门课的学分的总和。
每个学生都要选择规定数量的课程。其中有些课程可以直接选修,有些课程需要一定的基础知识,
必须在选了其它的一些课程的基础上才能选修。例如,《数据结构》必须在选修了《高级语言程序设计》
之后才能选修。
学生不可能学完大学所开设的所有课程,因此必须在入学时选定自己要学的课程。每个学生可选
课程的总数是给定的。现在请你找出一种选课方案,使得你能得到学分最多,并且必须满足先修课优
先的原则。假定课程之间不存在时间上的冲突。


这个是很典型的树形资源分配的DP
为了资源分配的好写,我们先要把多叉转二叉~
原来以为会比较难写,后来发现其实挺好写的
然后就资源分配了
f[i,j]表示以I为根节点分配j节课可以得到的最大学分
那么。。
f[left,k]+f[right,j-cost[i]-k]+v[k] (k in [0..j-cost[i]); (自己这一脉分配资源,且将一些资源分配给其他兄弟)
f[i,j]=max{
f[right,j] (自己这一脉不要,全部资源给其他兄弟)
这样就可以AC辣~

Code:

const shuru='course.in';
      shuchu='course.out';
var   a:array[0..1000,0..1000] of longint;
      ans:array[0..1000,0..1] of longint;
      v:array[0..1000] of longint;
      b:array[0..1000] of boolean;
      cost:array[0..1000] of longint;
      f:array[0..1000,0..1000] of longint;
      tot,m,x,i,j,k,n:longint;
procedure init;
begin
	assign(input,shuru);
	assign(output,shuchu);
	reset(input);
	rewrite(output);
	readln(n,m);
	fillchar(b,sizeof(b),false);
	for i:=1 to n do
		begin
			read(x,v[i]);
			if x<>0 then begin
					inc(a[x,0]);
					a[x,a[x,0]]:=i;
				     end
				else b[i]:=true;

		end;
	close(input);
end;
function max(a,b:longint):longint;
begin
    if a>b then exit(a);
    exit(b);
end;
procedure zhuan(p:longint);
var i,k:longint;
begin
	if a[p,0]=0 then exit;
	ans[p,0]:=a[p,1];
	zhuan(a[p,1]);
	k:=ans[p,0];
	for i:=2 to a[p,0] do
		begin
			zhuan(a[p,i]);
			ans[k,1]:=a[p,i];
			k:=a[p,i];
		end;
end;
procedure search(k,p:longint);
var i:longint;
begin
	if f[k,p]<>0 then exit;
	if p=0 then exit;
	if (ans[k,1]=0) and (ans[k,0]=0) then begin
						if p<>0 then f[k,p]:=v[k];
						exit;
					      end;
	if k<>0 then
		begin
			if (ans[k,0]=0) and (ans[k,1]<>0) then begin
								search(ans[k,1],p);
								search(ans[k,1],p-1);
								f[k,p]:=max(f[ans[k,1],p],f[ans[k,1],p-1]+v[k]);
								exit;
					  		      end;
			if (ans[k,0]<>0) and (ans[k,1]=0) then begin
								search(ans[k,0],p-1);
								f[k,p]:=f[ans[k,0],p-1]+v[k];
								exit;
							       end;
			search(ans[k,1],p);
			f[k,p]:=f[ans[k,1],p];
			for i:=0 to p-1 do
				begin
					search(ans[k,0],i);
					search(ans[k,1],p-i-1);
					f[k,p]:=max(f[k,p],f[ans[k,0],i]+f[ans[k,1],p-i-1]+v[k]);
				end;
            exit;
		end;
	search(ans[0,0],p);
	f[0,p]:=f[ans[0,0],p];
end;
procedure find(k,p:longint);
var i:longint;
begin
	if p=0 then exit;
	if k<>0 then
		begin
			if (ans[k,0]=0) and (ans[k,1]<>0) then begin
							 if f[k,p]=f[ans[k,1],p-1]+v[k] then begin writeln(k); inc(tot,v[k]); find(ans[k,1],p-1); exit; end;
													if f[k,p]=f[ans[k,1],p] then find(ans[k,1],p);
													exit;
													end;
			
			if (ans[k,0]<>0) and (ans[k,1]=0) then begin
														writeln(k);
														find(ans[k,0],p-1);
														inc(tot,v[k]);
														exit;
												   end;
			if f[k,p]=f[ans[k,1],p] then begin find(ans[k,1],p); exit; end;
			for i:=0 to p-1 do
				begin
					if f[k,p]=f[ans[k,0],i]+f[ans[k,1],p-i-1]+v[k] then begin
																			writeln(k);
																			inc(tot,v[k]);
																			find(ans[k,0],i);
																			find(ans[k,1],p-i-1);
                                                                            exit;
																		end;
		end;
        end;
	find(ans[0,0],p);
end;
procedure main;
begin
	init;
	for i:=1 to n do
		if b[i] then begin
				inc(a[0,0]);
				a[0,a[0,0]]:=i;
			     end;
	zhuan(0);
	for i:=1 to n do
		cost[i]:=1;
	cost[0]:=0;
	search(0,m);
	writeln(f[0,m]);
	find(0,m);
	close(output);
end;
begin
	main;
end.



T3
3. 警卫安排(guard)
【题目描述】
一个重要的基地被分为 n 个连通的区域。出于某种神秘的原因,这些区域以一个区域为核心,呈
一颗树形分布。
在每个区域安排警卫所需要的费用是不同的,而每个区域的警卫都可以望见其相邻的区域,只要
一个区域被一个警卫望见或者是安排有警卫,这个区域就是安全的。你的任务是:在确保所有区域都
是安全的情况下,找到安排警卫的最小费用。


这题是原题~
在建兰培训的时候有讲过~
是一种就是取还是不取,分情况什么的DP
和红书上的“没有上司的舞会”是同一类;
设   f(i,0)表示i结点被父亲看到;
     f(i,1)表示i被它的儿子看到;
f(i,2)表示在i安排警卫;
则状态转移方程为:
     f[i,0]=sigma(min(f[son,1],f[son,2]))
f[i,1]=sigma(min(f[son,1],f[son,2]))+f[其中一个son(这个son不在sigma里),2];
f[i,2]=sigma(min(f[son,1],f[son,2],f[son,3]));
然后就可以AC辣~
Code:

const shuru='guard.in';
	  shuchu='guard.out';
var	cost:array[0..1000] of longint;
	son:array[0..1000,0..1000] of longint;
	f:array[0..1000,0..2] of longint;
	father:array[0..1000] of longint;
	root,step,x,i,j,k,n:longint;
procedure init;
begin
	assign(input,shuru);
	assign(output,shuchu);
	reset(input);
	rewrite(output);
	readln(n);
	for i:=1 to n do
		begin
			read(x,cost[x],k);
			son[x,0]:=k;
			for j:=1 to k do
				begin
					read(son[x,j]);
					inc(father[son[x,j]]);
				end;
			readln;
		end;
	close(input);
	for i:=1 to n do
		if father[i]=0 then root:=i;
end;
function min(a,b:longint):longint;
begin
	if a<b then exit(a);
	exit(b);
end;
function minn(a,b,c:longint):longint;
begin
	if (a<=b) and (a<=c) then exit(A);
	if (b<=a) and (b<=c) then exit(B);
	if (c<=a) and (c<=b) then exit(C);
end;
procedure search(k,p:longint);
var i,j,step:longint;
begin
	if f[k,p]<>0 then exit;
	if p=0 then
				for i:=1 to son[k,0] do
					begin
					    search(son[k,i],2);
						search(son[k,i],1);
						f[k,p]:=f[k,p]+min(f[son[k,i],2],f[son[k,i],1]);
					end;
	if p=1 then begin
					f[k,p]:=maxlongint;
					for i:=1 to son[k,0] do
						begin
							search(son[k,i],2);
							step:=f[son[k,i],2];
							for j:=1 to son[k,0] do
								if j<>i then
									begin
										search(son[k,j],1);
										search(son[k,j],2);
										step:=step+min(f[son[k,j],1],f[son[k,j],2]);
									end;
							f[k,p]:=min(f[k,p],step);
						end;
				end;
	if p=2 then begin
					f[k,p]:=cost[k];
					for i:=1 to son[k,0] do
						begin
							search(son[k,i],0);
							search(son[k,i],1);
							search(son[k,i],2);
							inc(f[k,p],minn(f[son[k,i],0],f[son[k,i],1],f[son[k,i],2]));
						end;
				end;
end;

procedure main;
begin
	init;
	search(root,1);
	search(root,2);
	write(min(f[root,1],f[root,2]));
	close(output);
end;
begin
	main;
end.



T4
和T2很像,资源分配型
没什么好说的,,
Code:

const shuru='key.in';
	  shuchu='key.out';
var	father,next,t,cost,key,headlist:array[0..200] of longint;
	ans:array[0..200,0..1] of longint;
    f:array[-200..200,-2000..200] of longint;
	num,x,y,i,j,k,n,m:longint;
procedure init;
begin
	assign(input,shuru);
	assign(output,shuchu);
	reset(input);
	rewrite(output);
	readln(n,m);
	for i:=1 to n do
		begin
			readln(cost[i],key[i]);
			headlist[i]:=-1;
			father[i]:=-1;
		end;
	for i:=1 to n-1 do
		begin
			readln(x,y);
			inc(num);
			next[num]:=headlist[x];
			headlist[x]:=num;
			t[num]:=y;
			inc(num);
			next[num]:=headlist[y];
			headlist[y]:=num;
			t[num]:=x;
		end;
	close(input);
end;
procedure zhuan(k:longint);
var x,i,p:longint;
begin
	if (t[headlist[k]]=father[k]) and (next[headlist[k]]=-1) then exit;
	x:=headlist[k];
	while t[x]=father[k] do
		x:=next[x];
	father[t[x]]:=k;
	ans[k,0]:=t[x];
	zhuan(ans[k,0]);
	p:=ans[k,0];
    x:=next[x];
	while x<>-1 do
		begin
			if t[x]=father[k] then begin x:=next[x]; continue; end;
            father[t[x]]:=k;
			zhuan(t[x]);
			ans[p,1]:=t[x];
			p:=t[x];
			x:=next[x];
		end;
end;
function max(a,b:longint):longint;
begin
    if a>b then exit(a);
    exit(b);
end;
procedure search(k,p:longint);
var i:longint;
begin
	if k<=0 then exit;
	if p<0 then exit;
	if f[k,p]<>0 then exit;
	search(ans[k,1],p);
	f[k,p]:=f[ans[k,1],p];
	for i:=0 to p-cost[k] do
		begin
			search(ans[k,0],i);
			search(ans[k,1],p-i-cost[k]);
			f[k,p]:=max(f[k,p],f[ans[k,0],i]+f[ans[k,1],p-i-cost[k]]+key[k]);
		end;
	exit;
end;
procedure main;
begin
	init;
	if m<cost[1] then begin
						writeln(0);
						close(output);
						halt;
					end;
	zhuan(1);
	search(1,m);
	writeln(f[1,m]);
end;
begin
	main;
end.

总结:
其实树形DP主要分三种:
1:资源分配型 (例:NOI贪吃的九头龙,这次的2,4两题)
  主要求解方法 先多叉转二叉,然后看这个资源是自己吃一点,兄弟吃一点还是自己不吃全给兄弟
2:分情况讨论型 (例:没有上司的舞会 警卫安排)
  主要求解方法 把f[i,j,0] f[i,j,1]当成状态什么的在转移好了
3:树形递推 (例:玩具 
  主要求解方法:按照他说的做就好了,应该算是这三种里最简单的一种了
END~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值