SSL1229 技能树【树形DP】

55 篇文章 0 订阅
13 篇文章 0 订阅

在这里插入图片描述

几个要点:

1.

它虽然是“技能树”,但是它输入可能是一个“技能森林”,
因此,我们需要一个编号为0的点,作为所有树的根节点。

int y;
		if(s!="")
     	  y=find_(s);
     	else
     	  y=0;
     	son[y][0]++;
     	son[y][son[y][0]]=data[i];

2.

我们接下来分析如何DP
我们设 f [ i ] [ j ] f[i][j] f[i][j] 表示当前节点用 j j j 点能量值可获得的最大值
我们分两种情况来做:

  1. 不升级技能的情况下能得到的最大分数。
    f [ x ] [ j ] = max ⁡ ( f [ x ] [ j ] , f [ s o n [ x ] [ i ] ] [ j ] ) ; f[x][j]=\max(f[x][j],f[son[x][i]][j]); f[x][j]=max(f[x][j],f[son[x][i]][j]);
  2. 升级技能的情况下能得到的最大分数。
    f [ x ] [ w ] = max ⁡ ( f [ x ] [ w ] , f [ s o n [ x ] [ i ] ] [ w − q ] + p ) ; f[x][w]=\max(f[x][w],f[son[x][i]][w-q]+p); f[x][w]=max(f[x][w],f[son[x][i]][wq]+p);

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int data[100010],tot,son[1000][1000],l[1010],l1[1010][1010],l2[1010][1010],d[1010],f[1000][1000];
string s,ss[100100];
int n,m;
int find_(string s)
{
	for(int i=1; i<=tot; i++)
	 {
	 	if(s==ss[i])
	 	  return i;
	 }
	tot++; 
	ss[tot]=s;
	return tot;
}
void dp(int x,int k)
{
	int tmp[1010];
	if(k<0)
	  return;
	for(int i=1; i<=son[x][0]; i++)
	 {
	 	for(int j=0; j<=k; j++)
	 	   tmp[j]=f[x][j];
	 	if(d[son[x][i]]!=0)
	 	 {
	 	 	for(int j=0; j<=k; j++)   //儿子继承父亲
	 	 	   f[son[x][i]][j]=f[x][j];//       <
	 	 	dp(son[x][i],k);      //              \
	 	 	for(int j=0; j<=k; j++)  //回溯;       \
	 	 	   f[x][j]=max(f[x][j],f[son[x][i]][j]);//\
	 	 }                 //                          \
	 	int q=0,p=0; //                                  \ 
		for(int j=d[son[x][i]]+1; j<=l[son[x][i]]; j++)// |
		 {                                          //    |
		 	q+=l1[son[x][i]][j];  //统计                  |
		 	p+=l2[son[x][i]][j];  //                      |
		 	if(k-q<0)  //儿子不能取了就直接停止继续取       |
		 	  break;                   //                |               
		 	for(int w=0; w<=k-q; w++)  //同上一个for -----|
		 	  f[son[x][i]][w]=tmp[w];
		 	dp(son[x][i],k-q);
		 	for(int w=q; w<=k; w++)
		 	   f[x][w]=max(f[x][w],f[son[x][i]][w-q]+p);
		 }
	 }
}
int main()
{
    cin>>n;
    for(int i=1; i<=n; i++)
     {
     	getline(cin,s);
		getline(cin,s);
		data[i]=find_(s);
     	getline(cin,s);
     	int y;
		if(s!="")
     	  y=find_(s);
     	else
     	  y=0;
     	son[y][0]++;
     	son[y][son[y][0]]=data[i];
     	scanf("%d",&l[data[i]]);
     	for(int j=1; j<=l[data[i]]; j++)
     	   scanf("%d",&l1[data[i]][j]);
     	for(int j=1; j<=l[data[i]]; j++)
     	   scanf("%d",&l2[data[i]][j]);
     }
    scanf("%d",&m);
    for(int i=1; i<=n; i++)
       scanf("%d",&d[data[i]]);
    dp(0,m);
    cout<<f[0][m];
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值