树形动规_(技能树)

技能树(SGOI)
skill.pas/c/cpp
【问题描述】
玩过 Diablo 的人对技能树一定是很熟悉的。一颗技能树的每个结点都是一项技能,要学会这项技能则需
要耗费一定的技能点数。只有在学会了某一项技能以后,才能继续学习它的后继技能。每项技能又有着不同的级
别,级别越高效果越好,而技能的升级也是需要耗费技能点数的。
有个玩家积攒了一定的技能点数,他想尽可能地利用这些技能点数来达到最好的效果,因此他给所有技能的
所有级别都打上了分,他认为效果越好的则分数越高。现在他要你帮忙寻找一个分配技能点数的方案,使得分数
总和最高。
【输入格式】
第一行是一个整数 n(1<=n<=20),表示所有不同技能的总数。接下来依次给出了这 n 个不同技能的详细
描述。每个技能描述共包括 5 行,第一行是该技能的名称,第二行是该技能在技能树中的父技能的名称,为空
则表示该技能不需要任何的先修技能便能学习。第三行是一个整数 L(1<=L<=20) ,表示这项技能所拥有的最
高等级。第四行共有 L 个整数,其中第 i 个整数表示把这项技能从第 i-1 级升到第 i 级所需要的技能点数(0
级表示没有学习过)。第五行也包括了 L 个整数,其中第 i个整数表示该玩家对这项技能的第 i 级的效果评分,
分数不超过 20。在技能描述之后是玩家所用角色的描述,共有两行。第一行是一个整数 P(0<=P<=100),表
示目前所拥有的技能点数。接下来一行是 n 个整数,依次表示角色当前所习得的技能级别,0 表示尚未学习。这里

不会出现非法的情况,譬如在没有学习某项技能的时候已经习得了它的后继技能。
【输出格式】
只需包括一个整数 S,表示你的技能点最佳分配方案所得到的分数总和。
【输入样例】
3
Freezing Arrow
Ice Arrow
3
3 3 3
15 4 6
Ice Arrow
Cold Arrow
2
4 3
10 17
Cold Arrow
 
3
3 3 2
15 5 2
10
0 0 1
【输出样例】
42
【时间限制】
1s
【空间限制】
64M

//------------------------------------------------------------------------------------------------

分析:树形动规,多叉树转二叉树.

f[i,j]表示以i为根的子树,花j点技能点,能得到的最大分数.

枚举给左右儿子分配的点数进行转移,记忆化搜索.

code:

type  skill=record
      n,f:string;
      l:longint;
      v,p:array[0..21] of longint;
end;
const maxn=21;
      maxp=101;
var   s:array[0..maxn] of skill;
      f:array[0..maxn,0..maxp] of longint;
      learn,l,r:array[0..maxn] of longint;
      n,i,j,fa,tmp,p,ans:longint;


      function find(st:string):longint;
      var   o:longint;
      begin
            for o:=1 to n do
               if s[o].n=st then exit(o);
            exit(0);
      end;

      function maxx(a,b:longint):longint;
      begin
            if a>b then exit(a); exit(b);
      end;

      function DP(t,m:longint):longint;
      var   max,now,o,cost,value,q:longint;
      begin
            if f[t,m]>=0 then exit(f[t,m]);
            max:=DP(r[t],m);  //不学这种技能
            if learn[t]>0 then  //已经学过的话,向下接着学
            for o:=1 to m do
            begin
                  now:=DP(l[t],o)+DP(r[t],m-o);
                  max:=maxx(max,now);
            end;
            cost:=0;
            value:=0;
            for o:=learn[t]+1 to s[t].l do  //把这个技能接着学下去
            begin
                  cost:=cost+s[t].v[o];
                  value:=value+s[t].p[o];
                  for q:=0 to m-cost do
                  begin
                        now:=DP(l[t],q)+DP(r[t],m-cost-q)+value;
                        max:=maxx(max,now);
                  end;
            end;
            f[t,m]:=max;
            exit(max);
      end;

begin
      assign(input,'skill.in'); reset(input);
      assign(output,'skill.out'); rewrite(output);

      readln(n);
      for i:=1 to n do
      begin
            readln(s[i].n);
            readln(s[i].f);
            readln(s[i].l);
            for j:=1 to s[i].l do read(s[i].v[j]);
            readln;
            for j:=1 to s[i].l do read(s[i].p[j]);
            readln;
      end;

      readln(P);
      for i:=1 to n do read(learn[i]);

      for i:=1 to n do
      begin
            fa:=find(s[i].f);
            if l[fa]=0 then l[fa]:=i
            else
            begin
                  tmp:=l[fa];
                  while r[tmp]<>0 do tmp:=r[tmp];
                  r[tmp]:=i;
            end;
      end;

      fillchar(f,sizeof(f),255);
      for i:=0 to maxp do f[0,i]:=0;

      ans:=DP(l[0],P);

      writeln(ans);

      close(input);
      close(output);
end.

转载于:https://www.cnblogs.com/exponent/archive/2011/08/06/2129483.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值