树形DP

来源:互联网 发布:福运来彩票源码 编辑:程序博客网 时间:2024/04/29 02:12
嗯奉上今天的题解~
今天我们练习了树形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;beginif a>b then exit(a);exit(b);end;procedure init;beginassign(input,shuru);assign(output,shuchu);reset(input);rewrite(output);readln(n);for i:=1 to n dobeginread(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;beginif x>y then exit;if x=y then begin write(x,' '); exit; end;for i:=x to y doif f[x,y]=f[x,i-1]*f[i+1,y]+a[i] then beginwrite(i,' ');search(x,i-1);search(i+1,y);exit;      end;end;procedure main;begininit;for i:=n downto 1 dofor j:=i+1 to n dofor k:=i to j dof[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;beginmain;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;beginassign(input,shuru);assign(output,shuchu);reset(input);rewrite(output);readln(n,m);fillchar(b,sizeof(b),false);for i:=1 to n dobeginread(x,v[i]);if x<>0 then begininc(a[x,0]);a[x,a[x,0]]:=i;     endelse 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;beginif 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] dobeginzhuan(a[p,i]);ans[k,1]:=a[p,i];k:=a[p,i];end;end;procedure search(k,p:longint);var i:longint;beginif f[k,p]<>0 then exit;if p=0 then exit;if (ans[k,1]=0) and (ans[k,0]=0) then beginif p<>0 then f[k,p]:=v[k];exit;      end;if k<>0 thenbeginif (ans[k,0]=0) and (ans[k,1]<>0) then beginsearch(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 beginsearch(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 dobeginsearch(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;beginif p=0 then exit;if k<>0 thenbeginif (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 beginwriteln(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 dobeginif f[k,p]=f[ans[k,0],i]+f[ans[k,1],p-i-1]+v[k] then beginwriteln(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;begininit;for i:=1 to n doif b[i] then begininc(a[0,0]);a[0,a[0,0]]:=i;     end;zhuan(0);for i:=1 to n docost[i]:=1;cost[0]:=0;search(0,m);writeln(f[0,m]);find(0,m);close(output);end;beginmain;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';varcost: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;beginassign(input,shuru);assign(output,shuchu);reset(input);rewrite(output);readln(n);for i:=1 to n dobeginread(x,cost[x],k);son[x,0]:=k;for j:=1 to k dobeginread(son[x,j]);inc(father[son[x,j]]);end;readln;end;close(input);for i:=1 to n doif father[i]=0 then root:=i;end;function min(a,b:longint):longint;beginif a<b then exit(a);exit(b);end;function minn(a,b,c:longint):longint;beginif (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;beginif f[k,p]<>0 then exit;if p=0 thenfor i:=1 to son[k,0] dobegin    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 beginf[k,p]:=maxlongint;for i:=1 to son[k,0] dobeginsearch(son[k,i],2);step:=f[son[k,i],2];for j:=1 to son[k,0] doif j<>i thenbeginsearch(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 beginf[k,p]:=cost[k];for i:=1 to son[k,0] dobeginsearch(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;begininit;search(root,1);search(root,2);write(min(f[root,1],f[root,2]));close(output);end;beginmain;end.



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

const shuru='key.in';  shuchu='key.out';varfather,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;beginassign(input,shuru);assign(output,shuchu);reset(input);rewrite(output);readln(n,m);for i:=1 to n dobeginreadln(cost[i],key[i]);headlist[i]:=-1;father[i]:=-1;end;for i:=1 to n-1 dobeginreadln(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;beginif (t[headlist[k]]=father[k]) and (next[headlist[k]]=-1) then exit;x:=headlist[k];while t[x]=father[k] dox:=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 dobeginif 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;beginif 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] dobeginsearch(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;begininit;if m<cost[1] then beginwriteln(0);close(output);halt;end;zhuan(1);search(1,m);writeln(f[1,m]);end;beginmain;end.

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

0 0
原创粉丝点击