几个要点:
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 点能量值可获得的最大值
我们分两种情况来做:
- 不升级技能的情况下能得到的最大分数。
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]); - 升级技能的情况下能得到的最大分数。
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]][w−q]+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;
}