技能树(skill)

【问题描述】

玩过Diablo的人对技能树一定是很熟悉的。一颗技能树的每个结点都是一项技能,要学会这项技能则需要耗费一定的技能点数。只有学会了某一项技能以后,才能继续学习它的后继技能。每项技能又有着不同的级别,级别越高效果越好,而技能的升级也是 需要耗费技能点数的。 有个玩家积攒了一定的技能点数,他想尽可能地利用这些技能点数来达到最好的效果。因此他给所有的级别都打上了分,他认为 效果越好的分数也越高。现在他要你帮忙寻找一个分配技能点数的方案,使得分数总和最高。

【输入格式】

该题有多组测试数据。 

每组测试数据第一行是一个整数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表示还未学习。这里不会出现非法情况。 

【输出格式】

每组测试数据只需输出最佳分配方案所得的分数总和。 

【输入样例】

3

Freezing Arrow // 翻译为:冰冻箭

Ice Arrow // 翻译为:冰箭

3 3 3

15 4 6

Ice Arrow

Cold Arrow // 翻译为:冰冷的箭【你tm在逗我..】

2

4 3

10 17

Cold Arrow

 

3

3 3 2

15 5 2

10

0 0 1

【输出样例】

42

【解析】

这道题可以说是上一道题,也就是【选课(choose)(详情看上一篇报告)】的加强版。

这道题在【选课】的基础上增加了处理字符串、分等级这些比较坑爹的情况.. 当然最坑的不是这个,而是这道题还是多组数据输入输出的题目.. 也就是说我们要进行初始化..

那么说说总体思路吧..

首先就是输入流啦.. 记住输入name的时候要用【gets】输入,因为要考虑到有空格的情况..

然后输入初始的等级输入.. 这个时候我就用了一个比较巧妙地办法,也就是把输入的now[i]所在位置的得分和耗点为0,因为有时可以不选当前点而直接到孩子节点【除了now[i]为0外】..

for ( i = 1; i <= n; i ++ ){
	scanf ( "%d", &now[i] );
	tr[i].l[now[i]] = 0;
	tr[i].q[now[i]] = 0;
}

接着就是一个非常暴力的森林转二叉树【上一篇报告有提及..】..

最后就是最重要的,Tree_DP的过程:

void Tree_DP ( int x, int k ){
	if ( f[x][k] != -1 ) return; // 这句非常重要..不然会超时..
	if ( x < 0 || k < 0 ) return;
	int i, j, y;
	Tree_DP ( tr[x].rc, k );
	f[x][k] = f[tr[x].rc][k];
	int suml, sumq;
	suml = k; sumq = 0;
	for ( i = now[x]; i <= tr[x].lev; i ++ ){
		suml -= tr[x].l[i]; sumq += tr[x].q[i];
		if ( i == 0 ) continue;
		if ( suml < 0 ) break;
		for ( j = 0; j <= suml; j ++ ){
			Tree_DP ( tr[x].lc, j );
			Tree_DP ( tr[x].rc, suml-j );
			f[x][k] = _max ( f[x][k], f[tr[x].lc][j] + f[tr[x].rc][suml-j] + sumq );
		}
	}
}
大概就是这么多吧..

记住初始化要初始化到0节点.. 【因为这个错了好多次..】

That's all.

【完整代码】

#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
struct node {
	char name[25], fa_name[25];
	int lc, rc, lev, last;
	int l[25], q[25];
	node (){
		lc = rc = last = -1;
	}
}tr[25];
int n, now[25], m;
int f[25][110];
int fip ( node x ){
	int i, j, k;
	for ( i = 1; i <= n; i ++ ){
		if ( strcmp ( x.fa_name, tr[i].name ) == 0 ) return i;
	}
	return 0;
}
int _max ( int x, int y ){ return x > y ? x : y; }
void Tree_DP ( int x, int k ){
	if ( f[x][k] != -1 ) return;
	if ( x < 0 || k < 0 ) return;
	int i, j, y;
	Tree_DP ( tr[x].rc, k );
	f[x][k] = f[tr[x].rc][k];
	int suml, sumq;
	suml = k; sumq = 0;
	for ( i = now[x]; i <= tr[x].lev; i ++ ){
		suml -= tr[x].l[i]; sumq += tr[x].q[i];
		if ( i == 0 ) continue;
		if ( suml < 0 ) break;
		for ( j = 0; j <= suml; j ++ ){
			Tree_DP ( tr[x].lc, j );
			Tree_DP ( tr[x].rc, suml-j );
			f[x][k] = _max ( f[x][k], f[tr[x].lc][j] + f[tr[x].rc][suml-j] + sumq );
		}
	}
}
int main (){
	int i, j, k;
	while ( scanf ( "%d", &n ) != EOF ){
		for ( i = 0; i <= n; i ++ ){
			tr[i].lc = tr[i].rc = tr[i].last = -1; tr[i].lev = 0;
			memset ( tr[i].name, '\0', sizeof (tr[i].name) );
			memset ( tr[i].fa_name, '\0', sizeof ( tr[i].fa_name ) );
			memset ( tr[i].l, 0, sizeof (tr[i].l) );
			memset ( tr[i].q, 0, sizeof (tr[i].q) );
		}
		memset ( now, 0, sizeof (now) );
		for ( i = 1; i <= n; i ++ ){
			getchar ();
			gets ( tr[i].name );
			gets ( tr[i].fa_name );
			scanf ( "%d", &tr[i].lev );
			for ( j = 1; j <= tr[i].lev; j ++ ) scanf ( "%d", &tr[i].l[j] );
			for ( j = 1; j <= tr[i].lev; j ++ ) scanf ( "%d", &tr[i].q[j] );
		}
		scanf ( "%d", &m );
		for ( i = 1; i <= n; i ++ ){
			scanf ( "%d", &now[i] );
			tr[i].l[now[i]] = 0;
			tr[i].q[now[i]] = 0;
		}
		for ( i = 1; i <= n; i ++ ){
			k = fip (tr[i]);
			if ( tr[k].last == -1 ) tr[k].lc = i;
			else tr[tr[k].last].rc = i;
			tr[k].last = i;
		}
		memset ( f, -1, sizeof (f) );
		Tree_DP ( tr[0].lc, m );
		printf ( "%d\n", f[tr[0].lc][m] );
	}
	return 0;
}



你可能感兴趣的:(技能树(skill))