树形动态规划建树的思路与pascal的代码???求助啊求助~~~~谢谢啊谢谢~~~~
树形DP,要睡觉了,简单说一下
决策是向左走或者向右走
最好使用记忆化搜索
program jiandie;
type
tr=record
l,r,x:longint;
end;
var
n,m,i,q,ans:longint;
tree:array[1..100]of tr;
f:array[1..100]of longint;
procedure init;
var
i:longint;
begin
readln(n,m,q);
for i:=1 to n-1 do
readln(tree[i].l,tree[i].r,tree[i].x);
end;
function max(a,b:longint):longint;
begin
if a>b then exit(a);exit(b);
end;
function dp(x,y:longint):longint;
begin
if y>m then exit(0);
if f[x]-maxlongint then exit(f[x]);
f[x]:=max(dp(tree[x].l,y+1),dp(tree[x].r,y+1))+tree[x].x;
exit(f[x]);
end;
begin
init;
for i:=1 to n do f[i]:=-maxlongint;
if m=1 then ans:=tree[1].x
else if m=0 then ans:=0
else
begin
f[1]:=max(tree[1].x+dp(tree[1].l,2),tree[1].x+dp(tree[1].r,2));
ans:=f[1];
end;
ans:=ans-q;
if ans>=0 then writeln('TRUE')
else writeln('FALSE');
writeln(ans);
end.
思路如上,如果不懂就hi一下我
首先建立一个森林,然后从下向上动态规划每棵树,最后相加即可.
转移
最优解=max{他的下属1,他的下属2,...,他+他的下属的下属1,他+他的下属的下属2,...}
加分二叉树
【问题描述】
设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数
若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;
(1)tree的最高加分
(2)tree的前序遍历
[分析]很显然,本题适合用动态规划来解。如果用数组value[i,j]表示从节点i到节点j所组成的二叉树的最大加分,则动态方程可以表示如下:
value[i,j]=max{value[i,i]+value[i+1,j],value[i+1,i+1]+value[i,i]*value[i+2,j], value[i+2,i+2]+value[i,i+1]*value[i+3,j],…,value[j-1,j-1]+value[i,j-2]*value[j,j], value[j,j]+value[i,j-1]}
题目还要求输出最大加分树的前序遍历序列,因此必须在计算过程中记下从节点i到节点j所组成的最大加分二叉树的根节点,用数组root[i,j]表示
Ural 1018 二*苹果树
题目
有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树
2 5
\ /
3 4
\ /
1
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。
输入格式
第1行2个数,N和Q(1<=Q<= N,1<N<=100)。
N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。
每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。
每根树枝上的苹果不超过30000个。
输出格式
一个数,最多能留住的苹果的数量。
解析:因为题目一给出就是二叉的,所以很容易就可以写出方程:
a(I,j):=max(a(i.left,k)+a(i.right,j-k)),0<=k<=j
源程序代码:
由于比较简单便不给完全的代码了。
Function treedp(x,y:longint):longint;
Var I,j,k:longint;
Begin
J:=0;
For I:=0 to y do
begin
k:=treedp(b[x].l,I)+treedp(b[x].r,y-I);
if k>j then j:=k;
end;
treedp:=j;
End;
选课
[问题描述]
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?
输入:
第一行有两个整数N,M用空格隔开。(1<=N<=200,1<=M<=150)
接下来的N行,第I+1行包含两个整数ki和si, ki表示第I门课的直接先修课,si表示第I门课的学分。若ki=0表示没有直接先修课(1<=ki<=N, 1<=si<=20)。
输出:
只有一行,选M门课程的最大得分。
样例:
输入:
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2 输出:
13
解析:
这题比苹果树多了一个步骤就是把一棵普通树转化为二叉树。
读入数据时把二叉树建好:第一个孩子作为父节点的左子树,其它孩子作为第一个孩子的右子树。
F(x,y):表示节点x取y门课得最高学分,则
F(x,y)=max(f(x.l,k-1)+x.v+f(x.r,y-k))k=0,1,..y
f(x.l,k-1)+x.v(课程x的学分) :表示选了课程x,左孩子选k-1门课,共k门课。
f (x.r,y-k)表示右孩子只能选y-k门课。
标程中节点-1表示空节点,0是根节点,1—n是n门可选课程的节点.
思考:若本题加上选那些课程可得到这个最大学分,怎样修改程序?
实现:
怎么实现,是在竞赛中的很重要的一个问题,如果你想ac了这道题目的话,你应该熟悉怎么把一棵树转化成二叉树,完后怎么用递规的思想来实现动态规划。所以坚实的基础是很重要的东西,如果没有了基础,什么都是空中楼阁。
程序中已经边读边把二叉树建立好了。
源程序代码:
program bluewater;
type
tree=record
l,r,k:longint;
end;
var
s:string;
i,j,k,l:longint;
n,m:longint;
a:array[0..200] of tree;
b:array[-1..200,0..150] of integer;
f:array[0..200] of longint;
procedure treedp(x,y:longint);
var i,j,k,l:longint;
begin
if b[x,y]>=0 then exit;
treedp(a[x].r,y);{只有右子树的情况}
j:=b[a[x].r,y];
for k:=1 to y do{左右子树都有的情况}
begin
treedp(a[x].l,k-1);
treedp(a[x].r,y-k);
i:=b[a[x].l,k-1]+b[a[x].r,y-k]+a[x].k;
if i>j then j:=i;
end;
b[x,y]:=j;
end;
begin
readln(s);
assign(input,s);reset(input);
readln(n,m);
fillchar(f,sizeof(f),0);
for i:=0 to n do
begin a[i].l:=-1;a[i].r:=-1;a[i].k:=-1;end;
{build tree}
for i:=1 to n do
begin
readln(k,l);
a[i].k:=l;
if f[k]=0 then a[k].l:=i
else a[f[k]].r:=i;
f[k]:=i;
end;
{bianjie}
for i:=-1 to n do
for j:=-1 to m do
if (i=-1) or (j=0) then b[i,j]:=0 else b[i,j]:=-1;
{tree dp}
treedp(a[0].l,m);
{output}
writeln(b[a[0].l,m]);
end.
Tju1053 技能树
Problem
玩过Diablo的人对技能树一定是很熟悉的。一颗技能树的每个结点都是一项技能,要学会这项技能则需要耗费一定的技能点数。
只有学会了某一项技能以后,才能继续学习它的后继技能。每项技能又有着不同的级别,级别越高效果越好,而技能的升级也是
需要耗费技能点数的。
有个玩家积攒了一定的技能点数,他想尽可能地利用这些技能点数来达到最好的效果。因此他给所有的级别都打上了分,他认为
效果越好的分数也越高。现在他要你帮忙寻找一个分配技能点数的方案,使得分数总和最高。
Input
该题有多组测试数据。
每组测试数据第一行是一个整数n(1<=n<=20),表示所有不同技能的总数。
接下来依次给出n个不同技能的详细情况。
每个技能描述包括5行。
第一行是该技能的名称。
第2行是该技能在技能树中父技能的名称,名称为None则表示该技能不需要任何的先修技能便能学习。
第3行是一个整数L(1<=L<=20),表示这项技能所能拥有的最高级别。
第4行共有L个整数,其中第I个整数表示从地I-1级升到第I级所需要的技能点数(0级表示没有学习过)。
第5行包括L个整数,其中第I个整数表示从第I-1级升级到第I级的效果评分,分数不超过20。
在技能描述之后,共有两行,第1行是一个整数P,表示目前所拥有的技能点数。
接下来1行是N个整数,依次表示角色当前习得的技能级别,0表示还未学习。这里不会出现非法情况。
Output
每组测试数据只需输出最佳分配方案所得的分数总和。
解析:这题是选课的加强版,但并难不倒我们
还是把一棵树转换为二叉树,完后从子节点到根节点作一次dp,最后得到最优解
由于和上题很相像就不写方程了。
源代码程序:
program bluewater;
type
tree=record
s,sf:string;
l,r,m:longint;
c:array[1..20] of longint;
d:array[1..20] of longint;
end;
var
i,j,k,l,m,n:longint;
a:array[0..20] of tree;
b:array[0..20] of longint;
learn:array[0..20] of longint;
f:array[0..20,0..8000] of longint;
function treedp(x,y:longint):longint;
var i,j,k,l,max,o,p,q:longint;
begin
if f[x,y]<>-1 then begin treedp:=f[x,y];exit;end;
max:=treedp(a[x].r,y);
{learn>0}
if learn[x]>0 then
begin
for k:=0 to y do
begin
i:=treedp(a[x].l,k)+treedp(a[x].r,y-k);
if i>max then max:=i;
end;
end;
{learn=0}
l:=0;p:=0;i:=0;
for o:=1 to a[x].m do
begin
if o>learn[x] then
begin l:=l+a[x].c[o];p:=p+a[x].d[o];end;
for k:=0 to y-l do
begin
i:=treedp(a[x].l,k)+treedp(a[x].r,y-l-k)+p;
if i>max then max:=i;
end;
end;
f[x,y]:=max;
treedp:=max;
end;
function find(x:string):longint;
var i,j:longint;
begin
for i:=0 to n do
if a[i].s=x then break;
find:=i;
end;
begin
while not(eof(input)) do
begin
{input}
readln(n);
fillchar(a,sizeof(a),0);
fillchar(b,sizeof(b),0);
a[0].s:='None';
for i:=1 to n do
with a[i] do
begin
readln(s);
readln(sf);
readln(m);
for j:=1 to m do read(c[j]);readln;
for j:=1 to m do read(d[j]);readln;
end;
readln(m);
if m>8000 then m:=8000;
for i:=1 to n do read(learn[i]);readln;
{build binary tree}
for i:=1 to n do
begin
k:=find(a[i].sf);
if b[k]=0 then
begin b[k]:=i;a[k].l:=i;end
else begin a[b[k]].r:=i;b[k]:=i;end;
end;
{bian jie}
for i:=0 to 20 do
for j:=0 to 8000 do
f[i,j]:=-1;
for i:=0 to 8000 do f[0,i]:=0;
{main}
writeln(treedp(a[0].l,m));
end;
end.
战略游戏
Problem
Bob喜欢玩电脑游戏,特别是战略游戏。但是他经常无法找到快速玩过游戏的办法。现在他有个问题。
他要建立一个古城堡,城堡中的路形成一棵树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。
注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。
请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵.
Input
第一行为一整数M,表示有M组测试数据
每组测试数据表示一棵树,描述如下:
第一行 N,表示树中结点的数目。
第二行至第N+1行,每行描述每个结点信息,依次为:该结点标号i,k(后面有k条边与结点I相连)。
接下来k个数,分别是每条边的另一个结点标号r1,r2,...,rk。
对于一个n(0<n<=1500)个结点的树,结点标号在0到n-1之间,在输入数据中每条边只出现一次。
Output
输出文件仅包含一个数,为所求的最少的士兵数目。
例如,对于如下图所示的树:
答案为1(只要一个士兵在结点1上)。
Sample Input
2
4
0 1 1
1 2 2 3
2 0
3 0
5
3 3 1 4 2
1 1 0
2 0
0 0
4 0
Sample Output
1
2
Source
sgoi
分析:这题有2种做法,一种是比较简单但不是很严密的贪心,如果测试数据比较刁钻的话就不可能ac,而这题是一道比较典型的树型动态规划的题目,这题不但要考虑子节点对他的根节点的影响,而且每放一个士兵,士兵所在位置既影响他的子节点也影响了他的根节点。不过状态还是很容易来表示的,动规实现也不是很难,不过这在这些例题中也有了些“创新”了。而且这题不是一个对二叉树的dp,而是对一颗普通树的dp,所以更具代表性。
源程序代码:
program bluewater;
const
maxn=1500;
var
i,j,k,l:longint;
m,n,p,q:longint;
a:array[0..maxn,0..maxn] of boolean;
b:array[0..maxn] of longint;
c:array[0..maxn] of boolean;
function leaf(x:longint):boolean;
var i,j:longint;
t:boolean;
begin
t:=true;
for i:=0 to n-1 do
if a[x,i] then begin t:=false;break;end;
leaf:=t;
end;
function treedp(x:longint):longint;
var i,j,k,l:longint;
begin
j:=0;{add}
k:=0;{leaf}
l:=0;{not put not leaf}
for i:=0 to n-1 do
if (a[x,i]) and (x<>i) then
if leaf(i) then inc(k) else
begin
j:=j+treedp(i);
if not(c[i]) then inc(l);
end;
{puanduan}
if (k>0) or (l>0) then begin c[x]:=true;treedp:=j+1;exit;end;
if (j>0) and (l=0) then begin treedp:=j;exit;end;
end;
begin
{input}
readln(m);
for p:=1 to m do
begin
fillchar(b,sizeof(b),0);
fillchar(a,sizeof(a),false);
fillchar(c,sizeof(c),false);
readln(n);
for i:=1 to n do
begin
read(k,l);
for j:=1 to l do
begin
read(q);
a[k,q]:=true;
b[q]:=1;
end;
readln;
end;
{main}
for i:=0 to n-1 do
if b[i]=0 then break;
fillchar(b,sizeof(b),0);
if leaf(i) then writeln('1') else writeln(treedp(i));
end;
end.
Ural 1039 没有上司的晚会
背景
有个公司要举行一场晚会。
为了能玩得开心,公司领导决定:如果邀请了某个人,那么一定不会邀请他的上司
(上司的上司,上司的上司的上司……都可以邀请)。
题目
每个参加晚会的人都能为晚会增添一些气氛,求一个邀请方案,使气氛值的和最大。
输入格式
第1行一个整数N(1<=N<=6000)表示公司的人数。
接下来N行每行一个整数。第i行的数表示第i个人的气氛值x(-128<=x<=127)。
接下来每行两个整数L,K。表示第K个人是第L个人的上司。
输入以0 0结束。
输出格式
一个数,最大的气氛值和。
样例输入
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
样例输出
5
http://acm.timus.ru/submit.aspx?space=1&num=1039
分析:
f[i,1]表示邀请i的最大值
f[i,2]表示不邀请i的最大值
f[i,1]=sigma(f[i.sons,2])
f[i,2]=sigma(max{f[i.sons,1],f[i.sons,2]})
这个又是树型动态规划的一种分类,每个结点都有二种状态既选与不选。 .
我的教程里粘贴来的。
我最近也在研究这个,你可以从网上搜treedp,这里我给你传一份吧
加分二叉树
【问题描述】
设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数
若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;
(1)tree的最高加分
(2)tree的前序遍历
[分析]很显然,本题适合用动态规划来解。如果用数组value[i,j]表示从节点i到节点j所组成的二叉树的最大加分,则动态方程可以表示如下:
value[i,j]=max{value[i,i]+value[i+1,j],value[i+1,i+1]+value[i,i]*value[i+2,j], value[i+2,i+2]+value[i,i+1]*value[i+3,j],…,value[j-1,j-1]+value[i,j-2]*value[j,j], value[j,j]+value[i,j-1]}
题目还要求输出最大加分树的前序遍历序列,因此必须在计算过程中记下从节点i到节点j所组成的最大加分二叉树的根节点,用数组root[i,j]表示
Ural 1018 二*苹果树
题目
有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树
2 5
\ /
3 4
\ /
1
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。
输入格式
第1行2个数,N和Q(1<=Q<= N,1<N<=100)。
N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。
每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。
每根树枝上的苹果不超过30000个。
输出格式
一个数,最多能留住的苹果的数量。
解析:因为题目一给出就是二叉的,所以很容易就可以写出方程:
a(I,j):=max(a(i.left,k)+a(i.right,j-k)),0<=k<=j
源程序代码:
由于比较简单便不给完全的代码了。
Function treedp(x,y:longint):longint;
Var I,j,k:longint;
Begin
J:=0;
For I:=0 to y do
begin
k:=treedp(b[x].l,I)+treedp(b[x].r,y-I);
if k>j then j:=k;
end;
treedp:=j;
End;
选课
[问题描述]
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?
输入:
第一行有两个整数N,M用空格隔开。(1<=N<=200,1<=M<=150)
接下来的N行,第I+1行包含两个整数ki和si, ki表示第I门课的直接先修课,si表示第I门课的学分。若ki=0表示没有直接先修课(1<=ki<=N, 1<=si<=20)。
输出:
只有一行,选M门课程的最大得分。
样例:
输入:
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2 输出:
13
解析:
这题比苹果树多了一个步骤就是把一棵普通树转化为二叉树。
读入数据时把二叉树建好:第一个孩子作为父节点的左子树,其它孩子作为第一个孩子的右子树。
F(x,y):表示节点x取y门课得最高学分,则
F(x,y)=max(f(x.l,k-1)+x.v+f(x.r,y-k))k=0,1,..y
f(x.l,k-1)+x.v(课程x的学分) :表示选了课程x,左孩子选k-1门课,共k门课。
f (x.r,y-k)表示右孩子只能选y-k门课。
标程中节点-1表示空节点,0是根节点,1—n是n门可选课程的节点.
思考:若本题加上选那些课程可得到这个最大学分,怎样修改程序?
实现:
怎么实现,是在竞赛中的很重要的一个问题,如果你想ac了这道题目的话,你应该熟悉怎么把一棵树转化成二叉树,完后怎么用递规的思想来实现动态规划。所以坚实的基础是很重要的东西,如果没有了基础,什么都是空中楼阁。
程序中已经边读边把二叉树建立好了。
源程序代码:
program bluewater;
type
tree=record
l,r,k:longint;
end;
var
s:string;
i,j,k,l:longint;
n,m:longint;
a:array[0..200] of tree;
b:array[-1..200,0..150] of integer;
f:array[0..200] of longint;
procedure treedp(x,y:longint);
var i,j,k,l:longint;
begin
if b[x,y]>=0 then exit;
treedp(a[x].r,y);{只有右子树的情况}
j:=b[a[x].r,y];
for k:=1 to y do{左右子树都有的情况}
begin
treedp(a[x].l,k-1);
treedp(a[x].r,y-k);
i:=b[a[x].l,k-1]+b[a[x].r,y-k]+a[x].k;
if i>j then j:=i;
end;
b[x,y]:=j;
end;
begin
readln(s);
assign(input,s);reset(input);
readln(n,m);
fillchar(f,sizeof(f),0);
for i:=0 to n do
begin a[i].l:=-1;a[i].r:=-1;a[i].k:=-1;end;
{build tree}
for i:=1 to n do
begin
readln(k,l);
a[i].k:=l;
if f[k]=0 then a[k].l:=i
else a[f[k]].r:=i;
f[k]:=i;
end;
{bianjie}
for i:=-1 to n do
for j:=-1 to m do
if (i=-1) or (j=0) then b[i,j]:=0 else b[i,j]:=-1;
{tree dp}
treedp(a[0].l,m);
{output}
writeln(b[a[0].l,m]);
end.
Tju1053 技能树
Problem
玩过Diablo的人对技能树一定是很熟悉的。一颗技能树的每个结点都是一项技能,要学会这项技能则需要耗费一定的技能点数。
只有学会了某一项技能以后,才能继续学习它的后继技能。每项技能又有着不同的级别,级别越高效果越好,而技能的升级也是
需要耗费技能点数的。
有个玩家积攒了一定的技能点数,他想尽可能地利用这些技能点数来达到最好的效果。因此他给所有的级别都打上了分,他认为
效果越好的分数也越高。现在他要你帮忙寻找一个分配技能点数的方案,使得分数总和最高。
Input
该题有多组测试数据。
每组测试数据第一行是一个整数n(1<=n<=20),表示所有不同技能的总数。
接下来依次给出n个不同技能的详细情况。
每个技能描述包括5行。
第一行是该技能的名称。
第2行是该技能在技能树中父技能的名称,名称为None则表示该技能不需要任何的先修技能便能学习。
第3行是一个整数L(1<=L<=20),表示这项技能所能拥有的最高级别。
第4行共有L个整数,其中第I个整数表示从地I-1级升到第I级所需要的技能点数(0级表示没有学习过)。
第5行包括L个整数,其中第I个整数表示从第I-1级升级到第I级的效果评分,分数不超过20。
在技能描述之后,共有两行,第1行是一个整数P,表示目前所拥有的技能点数。
接下来1行是N个整数,依次表示角色当前习得的技能级别,0表示还未学习。这里不会出现非法情况。
Output
每组测试数据只需输出最佳分配方案所得的分数总和。
解析:这题是选课的加强版,但并难不倒我们
还是把一棵树转换为二叉树,完后从子节点到根节点作一次dp,最后得到最优解
由于和上题很相像就不写方程了。
源代码程序:
program bluewater;
type
tree=record
s,sf:string;
l,r,m:longint;
c:array[1..20] of longint;
d:array[1..20] of longint;
end;
var
i,j,k,l,m,n:longint;
a:array[0..20] of tree;
b:array[0..20] of longint;
learn:array[0..20] of longint;
f:array[0..20,0..8000] of longint;
function treedp(x,y:longint):longint;
var i,j,k,l,max,o,p,q:longint;
begin
if f[x,y]<>-1 then begin treedp:=f[x,y];exit;end;
max:=treedp(a[x].r,y);
{learn>0}
if learn[x]>0 then
begin
for k:=0 to y do
begin
i:=treedp(a[x].l,k)+treedp(a[x].r,y-k);
if i>max then max:=i;
end;
end;
{learn=0}
l:=0;p:=0;i:=0;
for o:=1 to a[x].m do
begin
if o>learn[x] then
begin l:=l+a[x].c[o];p:=p+a[x].d[o];end;
for k:=0 to y-l do
begin
i:=treedp(a[x].l,k)+treedp(a[x].r,y-l-k)+p;
if i>max then max:=i;
end;
end;
f[x,y]:=max;
treedp:=max;
end;
function find(x:string):longint;
var i,j:longint;
begin
for i:=0 to n do
if a[i].s=x then break;
find:=i;
end;
begin
while not(eof(input)) do
begin
{input}
readln(n);
fillchar(a,sizeof(a),0);
fillchar(b,sizeof(b),0);
a[0].s:='None';
for i:=1 to n do
with a[i] do
begin
readln(s);
readln(sf);
readln(m);
for j:=1 to m do read(c[j]);readln;
for j:=1 to m do read(d[j]);readln;
end;
readln(m);
if m>8000 then m:=8000;
for i:=1 to n do read(learn[i]);readln;
{build binary tree}
for i:=1 to n do
begin
k:=find(a[i].sf);
if b[k]=0 then
begin b[k]:=i;a[k].l:=i;end
else begin a[b[k]].r:=i;b[k]:=i;end;
end;
{bian jie}
for i:=0 to 20 do
for j:=0 to 8000 do
f[i,j]:=-1;
for i:=0 to 8000 do f[0,i]:=0;
{main}
writeln(treedp(a[0].l,m));
end;
end.
战略游戏
Problem
Bob喜欢玩电脑游戏,特别是战略游戏。但是他经常无法找到快速玩过游戏的办法。现在他有个问题。
他要建立一个古城堡,城堡中的路形成一棵树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。
注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。
请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵.
Input
第一行为一整数M,表示有M组测试数据
每组测试数据表示一棵树,描述如下:
第一行 N,表示树中结点的数目。
第二行至第N+1行,每行描述每个结点信息,依次为:该结点标号i,k(后面有k条边与结点I相连)。
接下来k个数,分别是每条边的另一个结点标号r1,r2,...,rk。
对于一个n(0<n<=1500)个结点的树,结点标号在0到n-1之间,在输入数据中每条边只出现一次。
Output
输出文件仅包含一个数,为所求的最少的士兵数目。
例如,对于如下图所示的树:
答案为1(只要一个士兵在结点1上)。
Sample Input
2
4
0 1 1
1 2 2 3
2 0
3 0
5
3 3 1 4 2
1 1 0
2 0
0 0
4 0
Sample Output
1
2
Source
sgoi
分析:这题有2种做法,一种是比较简单但不是很严密的贪心,如果测试数据比较刁钻的话就不可能ac,而这题是一道比较典型的树型动态规划的题目,这题不但要考虑子节点对他的根节点的影响,而且每放一个士兵,士兵所在位置既影响他的子节点也影响了他的根节点。不过状态还是很容易来表示的,动规实现也不是很难,不过这在这些例题中也有了些“创新”了。而且这题不是一个对二叉树的dp,而是对一颗普通树的dp,所以更具代表性。
源程序代码:
program bluewater;
const
maxn=1500;
var
i,j,k,l:longint;
m,n,p,q:longint;
a:array[0..maxn,0..maxn] of boolean;
b:array[0..maxn] of longint;
c:array[0..maxn] of boolean;
function leaf(x:longint):boolean;
var i,j:longint;
t:boolean;
begin
t:=true;
for i:=0 to n-1 do
if a[x,i] then begin t:=false;break;end;
leaf:=t;
end;
function treedp(x:longint):longint;
var i,j,k,l:longint;
begin
j:=0;{add}
k:=0;{leaf}
l:=0;{not put not leaf}
for i:=0 to n-1 do
if (a[x,i]) and (x<>i) then
if leaf(i) then inc(k) else
begin
j:=j+treedp(i);
if not(c[i]) then inc(l);
end;
{puanduan}
if (k>0) or (l>0) then begin c[x]:=true;treedp:=j+1;exit;end;
if (j>0) and (l=0) then begin treedp:=j;exit;end;
end;
begin
{input}
readln(m);
for p:=1 to m do
begin
fillchar(b,sizeof(b),0);
fillchar(a,sizeof(a),false);
fillchar(c,sizeof(c),false);
readln(n);
for i:=1 to n do
begin
read(k,l);
for j:=1 to l do
begin
read(q);
a[k,q]:=true;
b[q]:=1;
end;
readln;
end;
{main}
for i:=0 to n-1 do
if b[i]=0 then break;
fillchar(b,sizeof(b),0);
if leaf(i) then writeln('1') else writeln(treedp(i));
end;
end.
Ural 1039 没有上司的晚会
背景
有个公司要举行一场晚会。
为了能玩得开心,公司领导决定:如果邀请了某个人,那么一定不会邀请他的上司
(上司的上司,上司的上司的上司……都可以邀请)。
题目
每个参加晚会的人都能为晚会增添一些气氛,求一个邀请方案,使气氛值的和最大。
输入格式
第1行一个整数N(1<=N<=6000)表示公司的人数。
接下来N行每行一个整数。第i行的数表示第i个人的气氛值x(-128<=x<=127)。
接下来每行两个整数L,K。表示第K个人是第L个人的上司。
输入以0 0结束。
输出格式
一个数,最大的气氛值和。
样例输入
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
样例输出
5
http://acm.timus.ru/submit.aspx?space=1&num=1039
分析:
f[i,1]表示邀请i的最大值
f[i,2]表示不邀请i的最大值
f[i,1]=sigma(f[i.sons,2])
f[i,2]=sigma(max{f[i.sons,1],f[i.sons,2]})
这个又是树型动态规划的一种分类,每个结点都有二种状态既选与不选。 .
我的教程里粘贴来的。
如果你要建的树为二叉树,那么你可以使用数组来建树(类似堆),但是如果不是,那就最好用链表,其实你也可以用链表建树之后再转换成为一颗二叉树。
如果你要建的树为二叉树,那么你可以使用数组来建树(类似堆),但是如果不是,那就最好用链表,其实你也可以用链表建树之后再转换成为一颗二叉树。
智能网联汽车发展趋势
比如,帮助他们避免与其它车辆发生事故和碰撞,或动态规划前往目的地的优秀路线。 其次是自动驾驶汽车。5G和Al最终将带来可靠的自动驾驶汽车。这些车辆将配备一个基于Al的车载计算机,根据车载传感器收集的数据以及路边设备和其他车辆通过5G网络提供的数据,可以感知到车辆周围的环境,并能够适应任何情况。自动驾驶汽车还将带来...
ACM入门阶段去哪做题?
杭电的OJ,上面的水题多,特别是输入输出那部分,在第一页的最下面。ACM (Association for Computing Machinery ) 中文:国际计算机学会。ACM是一个世界性的计算机从业员专业组织,创立于1947年,是世界上第一个科学性及教育性计算机学会,目前在全世界130多个国家和地区拥有超过10万名的会员。ACM是全...
我要达到怎样的水平才能去参加acm编程比赛
常用算法中的另一类是以“相似或相同子问题”为核心的,包括递推、递归、贪心法和动态规划。这其中比较难于掌握的就是动态规划,如何抽象出重复的子问题是很多题目的难点所在,笔者建议初学者仔细理解图论中一些以动态规划为基本思想所建立起来的基本算法(比如Floyd-Warshall算法),并且多阅读一些定理的证明,这虽然不能有...
怎么样才能参加ACM 竞赛啊??
常用算法中的另一类是以“相似或相同子问题”为核心的,包括递推、递归、贪心法和动态规划。这其中比较难于掌握的就是动态规划,如何抽象出重复的子问题是很多题目的难点所在,笔者建议初学者仔细理解图论中一些以动态规划为基本思想所建立起来的基本算法(比如Floyd-Warshall算法),并且多阅读一些定理的证明,这虽然不能有...
acm初学者要准备什么 看什么书啊
常用算法中的另一类是以“相似或相同子问题”为核心的,包括递推、递归、贪心法和动态规划。这其中比较难于掌握的就是动态规划,如何抽象出重复的子问题是很多题目的难点所在,笔者建议初学者仔细理解图论中一些以动态规划为基本思想所建立起来的基本算法(比如Floyd-Warshall算法),并且多阅读一些定理的证明,这虽然不能有...
我要达到怎样的水平才能去参加acm编程比赛
常用算法中的另一类是以“相似或相同子问题”为核心的,包括递推、递归、贪心法和动态规划。这其中比较难于掌握的就是动态规划,如何抽象出重复的子问题是很多题目的难点所在,笔者建议初学者仔细理解图论中一些以动态规划为基本思想所建立起来的基本算法(比如Floyd-Warshall算法),并且多阅读一些定理的证明,这虽然不能有...
ACM要怎么搞?
常用算法中的另一类是以“相似或相同子问题”为核心的,包括递推、递归、贪心法和动态规划。这其中比较难于掌握的就是动态规划,如何抽象出重复的子问题是很多题目的难点所在,笔者建议初学者仔细理解图论中一些以动态规划为基本思想所建立起来的基本算法(比如Floyd-Warshall算法),并且多阅读一些定理的证明,这虽然不能有...
数据结构与算法教程人民邮电出版社出版图书
该书主要针对非计算机专业的高校学生,讲解数据结构中的核心内容,如线性表、树与二叉树、图等,以及查找和排序方法。书中注重实践,通过实例展示了回溯法、分治法、贪心法和动态规划等算法设计思想,使读者能够深入理解数据结构与算法之间的关系。本书以清华大学电子系的数据结构讲义为基础,旨在帮助学生掌握...
衷冠氟比: 用三个array模拟就可以了.一个记点,一个记父亲,一个记儿子.当然,array的类型还要视具体情况而定,一般会用到record.不建议用指针建树.
秦城区13397926940: Pascal动态规划题,求思路?
衷冠氟比: f[i,j]为跳i次到第j棵树距离吃到的最多香蕉数 f[i,j] = max{f[i-1,j-k]}+a[j];(b[j]-b[j-k]<=D) 取f[M,1..N]的最大值
秦城区13397926940: PASCAL动态规划思路 - ?
衷冠氟比: 动态规划算法通常用于求解具有某种最优性质的问题.在这类问题中,可能会有许多可行解.每一个解都对应于一个值,我们希望找到具有最优值(最大值或最小值)的那个解.设计一个动态规划算法,通常可以按以下几个步骤进行:(1)找出最优解的性质,并刻画其结构特征.(2)递归地定义最优值.(3)以自底向上的方式计算出最优值.(4)根据计算最优值时得到的信息,构造一个最优解.其中(1)——(3)步是动态规划算法的基本步骤.在只需要求出最优值的情形,步骤(4)可以省去.若需要求出问题的一个最优解,则必须执行步骤(4).此时,在步骤(3)中计算最优值时,通常需记录更多的信息,以便在步骤(4)中,根据所记录的信息,快速构造出一个最优解.
秦城区13397926940: Pasca算法中的动态规划、递归、回溯、枚举、贪心有什么区别?最好举个例子!!速度点!!急啊 - ?
衷冠氟比: 我记得以前有一篇180页的pascal算法,我用delphi实现了其中一部分,这些算法都相当有用,但是也是超难,我啃回溯啃了5天.动态规划在分类步骤比较小的情况下用,递归太常用了,回溯就是循环的递归(解决回路嵌套问题),枚举只有情况有限的时候一个个列举出来,贪心就是不求最佳,找一个满足添加的结果就行
秦城区13397926940: pascal动态规划转移方程要按照什么要点或规律来编写? - ?
衷冠氟比: 使用动态程序设计方法解题的步骤 设计一个标准的动态规划算法,通常可按以下几个步骤进行: 划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段.注意这若干个阶段一定要是有序的或者是可排序的(即无后向性),否则问题就...
秦城区13397926940: 数学建模步骤 - ?
衷冠氟比: 摘要在整篇论文评阅中占有重要权重,务必认真书写(篇幅不能超过一页).全国评阅时将首先根据摘要和论文整体结构及概貌对论文优劣进行初步筛选.摘要写得不好,论点不明,条理不清,评委不再阅读正文,论文即遭被淘汰. 摘要是全文...
秦城区13397926940: pascal拦截导弹问题的解题思路? - ?
衷冠氟比: 主要思想是动态规划中的求最长不上升序列. typefnode=recordshuliang,qianqu:integer;//从开始到当前导弹,能得到的最大长度end; vara,b:array[1..100] of integer;f:array[1..100] of fnode;s:ansistring;p,i,j,n,c,max:integer; beginassign(...
秦城区13397926940: pascal 动态规划 奶牛打工 怎么做,思路是什么? - ?
衷冠氟比: 分析 这道题目和上面的采药的区别仅仅在于,每个物品可以无限次的取,其他的和采药并没有什么区别. 当然,这样的问题仍然可以用背包模型来解决,我们将这种模型称为无限背包. 在这种情况下,我们可以把上面的opt[i-1,j-v[i]]变成opt[i,j-v[i...
秦城区13397926940: 动态规划的用法?
衷冠氟比: 求一个问题的最优解,这个问题的最优解必定与之前的子问题有关系,可能是子问题的几倍,也可能是几个子问题的和,或是其他什么乱七八糟的东西,这个当前问题与子问题的关系,我们称之为动态转移方程. 通常,我们会用一个数组或一个变量来存储子问题的最优解,例如f[m][n]存储当问题的条件为m和n时的最优解在存储时,往往有不止一种情况,要想好怎么存储,动态转移方程就很容易写出来. 在子问题中有一种问题,幼稚到不能再幼稚,但后面的计算都需要它们,我们给它们赋值,使后面的计算可以进行,这就叫边界 动态规划,有线形动态规划,树形动态规划,合并类动态规划,其中,树形动态规划是最难的 建议你去看一看我的空间,里面有很多动态规划的详解
秦城区13397926940: 动态规划 石子合并pascal - ?
衷冠氟比: 有时间复杂度要求么?这是个递归算法,好久没写了,不知道会不会超时 Program Stones; Type Node = Record{当前序列的合并方案} c : Longint;{得分和} d : Byte{子序列1的堆数} End; SumType = Array [1..100,1..100] of Longint; {sumtype[i,j]-子...