腾讯机试题1

小Q需要穿越怪兽谷,通过贿赂怪兽以避免攻击。问题是确定最少需要多少金币。文章提出了以性价比为基础的策略,并详细介绍了如何使用回溯算法解决问题,包括设置武力值上限和最小金币条件。提供了C++代码实现,但未经过测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

小Q打算穿越怪兽谷,他不会打怪,但是他有钱。他知道,只要给怪兽一定的金币,怪兽就会一直护送他出谷。

在谷中,他会一次遇见N只怪兽,每只怪兽都有自己的武力值和要“贿赂”它所需的金币。如果小Q没有“贿赂”它而它的武力值又高于护送小Q的所有怪兽的武力值之和,这只怪兽就会攻击小Q

小Q想知道,要想成功穿越怪兽谷而不被攻击,最少要准备多少金币?

输入描述:

第一行:输入一个整数N,代表怪兽的数量
第二行:输入N个整数:d1、d2、....dN,代表对应怪兽的武力值
第三行:输入N个整数:p1、p2、.....pN,代表收买对应怪兽所需要的金币

1≤N≤501\leq N \leq501N501≤d1,d2,....dn≤10121\leq d_1,d_2,....d_n\leq 10^{12}1d1,d2,....dn10121≤p1,p2,....pn≤21\leq p_1,p_2,....p_n\leq 21p1,p2,....pn2

输出描述:

一个整数,表示所需要的最小金币数量

一、思路

因为怪兽是否攻击你,取决于你所拥有的的怪兽武力值的总和,因此我们应该注重每只怪兽的性价比:

例如:一只怪兽的武力值为20,需要1金币,另一只武力值为30,需要2金币,明显第一只的性价比要高一些

因为第一只怪兽,花1金币就能买到20武力值,而第二只怪兽,花1金币只能买到15武力值

那么全买性价比高的有没有问题呢?

当然有,因为性价比高的不一定都在前面,如果都在后面,那么之前的钱该出的还是得出

实际上在第二行里面已经给出每个关卡所需要的最小武力值之和

所以策略应该是:

(1)找出武力值最高的怪兽,并以该武力值作为购买上限,当你所拥有的的武力值之和大于或者等于这个上限时,不必购买

(2)计算每只怪兽的性价比,当你所购买的武力值还没有到达(1)中的上限时,考虑购买性价比高的怪兽:

但是这一步实际上也有点问题:

你需要考虑购买之后,武力值是否超过了上限,实际情况还会更加复杂

那么我们换个角度来看待这个问题,之前我们的想法是根据常识来设计一些复杂的算法,以解决这个问题。

实际上N=50,如果尝试使用暴力解法,按照排列组合来看,时间复杂度最高也不过是O(250)O(2^{50})O(250),如果我们能够在其中加入一些条件,还可以大幅降低这个数值

(一)回溯算法

说起暴力解法,最适合的应该是回溯法了,每次遇到怪兽,我们有两种选择:

(1)收买

(2)不收买(有的关卡必须收买,没得选,比如:第一只怪兽)

我们可以设置一个武力值上限 和 最小金币来作为筛选最终结果的条件

C++代码:(没有找到OJ进行测试,可能有错,不过我觉得思路应该没有问题,如果有错,欢迎指正)

class Solution {
public:
	int minMoney(vector<int>& Force_value, vector<int>& Need_money) {
		int max_force = 0, current_pos = 0, current_force = 0, current_money = 0;
		int sum = 0;
		for (int i = 0; i < Force_value.size(); i++) {
			if (Force_value[i] > max_force)
				max_force = Force_value[i];
			sum += Need_money[i];
		}
		// 初值:假设每个怪兽都要收买
		int  min_money = sum;
		recall(Force_value, Need_money, current_pos, current_force, current_money, min_money, max_force);
		return min_money;
	}

	void recall(vector<int>& Force_value, vector<int>& Need_money, int current_pos, int current_force, int current_money, int& min_money, int& max_force) {
		if (current_force >= max_force) {
			if (current_money < min_money)
				min_money = current_money;
			return;
		}
		// 开始闯关
		int k = 0;
		for (int i = current_pos; i < Force_value.size(); i++) {
			if (current_force < Force_value[i]) {	// 当前武力值低于怪兽,必须收买
				current_force += Force_value[i];
				current_money += Need_money[i];
				// 如果目前为止,所花金币已经超过之前找到最小值,不用回溯,可以直接返回
				if (current_money >= min_money)
					return;
				//不需要回溯
			}
			else {	// 可以选择收买,也可以不收买。		
				//	不收买则什么都不用做
				current_force += Force_value[i];
				current_money += Need_money[i];
				// 如果目前为止,所花金币已经超过之前找到最小值,不用回溯,可以直接返回
				if (current_money >= min_money)
					return;
				recall(Force_value, Need_money, i + 1, current_force, current_money, min_money, max_force);
				// 回溯,选择不收买
				current_force -= Force_value[i];
				current_money -= Need_money[i];
			}
		}
	}
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值