(C++)题目超详解(超容易理解) 匠人的自我修养

很久没有更新博客了。

今天讲的是《信息学奥赛一本通·初赛真题解析》P351的一道完善程序初赛题(出自2019CSP-S):匠人的自我修养。

该题虽然说是完善程序题,但是对于算法学习比较有价值,涉及拓扑排序,对于初赛通过也有一定帮助,因此这篇文章就产生了。(注:本文部分内容涉及《信息学奥赛一本通·初赛真题解析》的书内容以及答案解析内容。)

题目:

一个匠人决定要学习n个新技术。要想成功学习一个新技术,他不仅要拥有一定的经验值,而且还必须要先学会若干个相关的技术。学会一个新技术之后,他的经验值会增加一个对应的值。给定每个技术的学习条件和习得后获得的经验值,给定他已有的经验值,请问他最多能学会多少个新技术。

输入第一行有两个数,分别为新技术个数n(1≤n≤10^3),以及已有经验值(≤10^7)。

接下来n行,第i行的两个正整数,分别表示学习第i个技术所需的最低经验值(≤10^7),以及学会第i个技术后可获得的经验值(≤10^4)。

接下来n行,第i行的第一个数mi(0≤mi<n),表示第i个技术的相关技术数量,紧跟着mi个两两不同的数,表示第i个技术的相关技术编号。输出最多能学会的新技术个数。

(以下代码时间复杂度为O(n^2),并附注释)

//本题关键在于find函数,类似于拓扑排序,找到一个入度为0并且能够选取的技术,
//然后从DAG上删除,unlock维护每一个技术的度数。 
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1001;
int n;
int cnt[maxn];//cnti 表示 以i号为前置技术的技术数 
int child[maxn][maxn];//childij 表示以i号为前置技术的第j个技术编号 
int unlock[maxn];//unlocki 表示每一个技术的前置技术数 
int points;//points 存储临时经验值 
int threshold[maxn],bonus[maxn];//thresholdi 表示第i号技术的经验门槛 bonusi 表示学习i号技术过后能够获得的经验数 
bool find(){
	int target = -1;
	for(int i=1;i<=n;i++)
		if(unlock[i] == 0 && points >= threshold[i]){//此处代表该技术可学。 //条件是前置技术已经全部学完,经验值到达门槛。 
			target=i;//目标找到,记录 
			break;//直接结束循环 
		}
	if(target == -1)//如果目标没有找到 
		return false;//返回false,不能学了 
	unlock[target] = -1;//如果等于-1,代表已经处理过了。 
	points += bonus[target];//加上该技能的经验值。 
	for(int i=0;i<cnt[target];i++)//遍历以新技术作为前置技术的所有技术 
		unlock[child[target][i]]--;//每一个以已解锁技术为先修技能的技术,入度-1 
	return true;//学到新技术,返回true 
}	
int main(){
	cin>>n>>points;
	for(int i=1;i<=n;i++){
		cnt[i]=0;//初始化cnt 
		cin>>threshold[i]>>bonus[i];//输入 
	}
	for(int i=1;i<=n;i++){
		int m;
		cin>>m;
		unlock[i] = m;//i有m个前置技术 
		for(int j=0;j<m;j++){
			int fa;
			cin>>fa;//输入前置技术编号 
			child[fa][cnt[fa]] = i;//以fa为前置技术的技术中第cnt[fa](以fa为前置技术的技术数)项技术为i(cnt[fa]从0开始) 
			cnt[fa]++;//前置技术数++ 
		}
	}
	int ans = 0;
	while(find()) ans++;//能够学会一项新技术,ans++ 
	cout<<ans<<endl;//输出结果 
	return 0;
}

如果不了解拓扑排序,请另找文章先了解该算法,再来看此篇题解。

本篇内容结束,再见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值