蓝桥杯 算法训练 强力党逗志芃

蓝桥杯 算法训练 强力党逗志芃

题目描述

  • 资源限制
    时间限制:1.0s 内存限制:256.0MB
  • 问题描述
    逗志芃励志要成为强力党,所以他将身上所以的技能点都洗掉了重新学技能。现在我们可以了解到,每个技能都有一个前提技能,只有学完了前提技能才能学习当前的技能(有一个最根本的技能不需要前提技能)。学习每个技能要消耗一个技能点,然后可以获得这个技能的威力值。由于逗志芃要陪妹子,所以他希望你教他如何点技能使得威力值最大从而成为强力党。
  • 输入格式
    第一行两个数n,m表示有n个技能和m个技能点。第二行有n个数,第i个数表示第i个技能的威力值。
    之后的n-1行,每行两个数x,y,表示y技能的前提技能是x,也就是说先学第x个技能才能学弟y个技能。
  • 输出格式
    一个数,最大的威力值。
  • 样例输入
    3 2
    1 10 20
    1 2
    1 3
  • 样例输出
    21
  • 数据规模和约定
    0<n,m<=200, 技能的威力值不超过200。

方案1 暴力递归(过不了,只是提供一个暴力思路)

#include<iostream>
#include<algorithm>

using namespace std;

int n;				//n个技能 
int m;				//m个技能点 
int score[200];		//score[i]: 第i+1个技能的威力 
bool flag[200];		//已经学习的技能 
int fro[200];		//fro[i]=j: i技能的前置技能是j 

//函数参数: 已学习的技能数cur
//函数功能:返回能获得的最大威力 
int f(int cur){
	//没有可用的技能点了
	if(cur == m){
		return 0;
	}
	
	int MAX = 0;
	for(int i=0; i<n; ++i){
		//如果该技能没有被学习,且该技能的前置技能已经学习
		if(!flag[i] && (fro[i]==-1 || flag[fro[i]])){
			flag[i] = 1;	//学习该技能
			MAX = max(MAX, f(cur+1)+score[i]);
			flag[i] = 0;
		}
	}
	//返回最大值
	return MAX;
}
					
int main(){	
	cin>>n>>m;
	for(int i=0; i<n; ++i){
		cin>>score[i];
		fro[i] = -1;
	}
	for(int i=0; i<n-1; ++i){
		int a, b;
		cin>>a>>b;
		fro[b-1] = a-1;
	}
	cout<<f(0)<<endl;
	return 0; 
} 

方案1 动态规划,树形dp

#include<iostream>
#include<algorithm>

using namespace std;

int n;				//n个技能 
int m;				//m个技能点 
int score[201];		//score[i]: 技能i的威力 
bool mp[201][201];	//邻接矩阵, mp[i][j]=1  表示技能j的前置技能是技能i,
					//特殊地:如果一个技能没有前置技能,则默认前置技能是0 
int ans[201][202];	//ans[i][j]: 以第i个技能为起点,还有j个技能点时所能获得的最大威力 

//思路:可以想象为以0技能为起点的一棵树,即不连通的图(邻接矩阵存储边) 
//函数参数: 起点技能cur,求出ans[cur][*]的值 (*=0,1,...,m,m+1)
void f(int cur){
	//遍历每一个技能点 
	for(int i=1; i<=n; ++i){
		//如果该技能i的前置技能是cur 
		if(mp[cur][i]){
			//1.以i为起点技能继续进行递归
			f(i); 
		
			//2.根据函数定义:上面函数f结束后,以技能i为起点的ans[i][*]都已经求出 
			
			//3.此时更新ans[cur][*]的值 
			//3.1 由于要保留一个技能点给自己,所以最多还有m-1个技能点可供分配
			//3.2 考虑到技能cur下面的子技能(子节点)数未知且有限
			//		故必须要从m-1个技能点递减开始考虑分配,保证不会重复相加
			//举个例子: 假设1:技能cur下面只有一个子技能i, 且技能威力为3 
			//			 则	ans[i][0]=0, ans[i][1]=3, ans[i][2]=3, ..., ans[i][m]=3
			//(1) 如果递增考虑分配: 
			// 则ans[cur][0]=0, ans[cur][1]=3, ans[cur][2]=ans[cur][1]+ans[i][1]=6
			// 此时已经出现错误, 因为cur技能下面只有一个技能i,故当把技能点给i后,
			// 即使还有技能点,但也没有技能了,故应该 ans[cur][2]= 0+ans[i][1]=3
			//(2) 如果递减考虑分配,则可以避免这个错误
			// 因为ans[cur][j]=ans[cur][j-k]+ans[i][k]
			// 在本次循环中 ans[cur][j] 要比 ans[cur][j-k] 先更新 (因为j递减) 
			// 所以更新ans[cur][j]时用的是上一轮循环(指的是i循环)的 ans[cur][j-k]
			// 即可以保证把k个技能点分给技能i后,剩余(j-k)个技能点还可以分配给其他技能
			// 因为上述(j-k)个技能点在上一轮循环(指的是i循环)中已经分配好了,即完成更新了 
			for(int j=m-1; j>=0; --j){		//还有j个技能点时 
				for(int k=0; k<=j; ++k){	//选择是否分给i技能k个技能点(这个递增递减考虑都行) 
					ans[cur][j] = max(ans[cur][j], ans[cur][j-k]+ans[i][k]);
				}
			}
			
		}
	}
	
	//4.此时cur节点已经完成更新,只需加上技能cur的威力即可
	// 	即 ans[cur][i] = ans[cur][i-1] + score[cur];  注意仍需递减更新,理由同上 
	for(int i=m; i>=0; --i){	//还剩i个技能点时
		if(i==0){
			ans[cur][0] = 0;
		}else{
			ans[cur][i] = ans[cur][i-1] + score[cur]; 
		}
	}
}
					
int main(){	
	cin>>n>>m;
	m += 1;				//由于人为创造了一个0技能,故需要总技能点数+1 
	
	//假设所有技能都没有前置技能,即规定前置技能都是0,即 mp[0][i] = 1
	for(int i=1; i<=n; ++i){
		mp[0][i] = 1;
	}
	
	for(int i=1; i<=n; ++i){
		cin>>score[i];
	}
	
	for(int i=0; i<n-1; ++i){
		int a, b;
		cin>>a>>b;
		mp[a][b] = 1;	//b的前置技能是a 
		mp[0][b] = 0;	//b有前置技能,修改 mp[0][b]的值为0 
	}
	
	//从0技能点开始,也是人为构造0技能的意义,防止有多棵树存在,即存在森林 
	f(0);
	
	//输出从0技能点开始有m个技能点时的最大威力 
	cout<<ans[0][m]<<endl;

	return 0; 
} 
  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值