1334 - C. Circle of Monsters(贪心)

本文介绍了一种关于怪物击杀顺序的算法问题,通过贪心策略确定最优的击杀顺序以最小化所需的子弹数量。讨论了如何利用预计算总伤害值并枚举首个击杀目标来优化计算过程。

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

题目

思路:当我们选定了一个怪物作为开始先杀掉后,对于余下的怪物我们一定要按顺序一个个杀掉,因为对于一开始被杀的后一个怪物(如果没炸死的话)我们最终一定要杀掉的,如果他是在他后面一个怪物被杀之后被杀那么它的爆炸不会产生贡献,我们在贪心的决断下,一定要先杀他让它的爆炸伤害物尽其用。那么其实我们只需枚举一下各个怪物第一个杀,取最小即可。但暴力的话复杂度O(n^2),但可以发现其实对于每一个结果只是改动了一个小地方。我们先求出一个假设每个怪物的爆炸伤害都做出了贡献的sum=min(i-1的爆炸伤害-i的生命值,0)i=1,2,3…n 。然后枚举每一个i,ans=min(ans,sum+i怪物的生命-min(i-1的爆炸伤害-i的生命值,0)),因为第一个杀i怪的所需子弹数,与sum相比就是加一个先杀i怪的生命减一个min(i-1的爆炸伤害-i的生命值,0).

Code:

#include<iostream>
#include<vector>
#include<algorithm>
#define FAST ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const int Max = 1e6 + 5;
ll a[Max], b[Max];

int main()
{
	FAST;int t;cin >> t;
	while (t--)
	{
		int n;cin >> n;
		ll sum = 0, ans = 0;
		for (int i = 1;i <= n;i++)	cin >> a[i] >> b[i];
		for (int i = 1;i <= n;i++)
		{
			if (i == 1)sum += max(ll(0), a[i] - b[n]);
			else sum += max(ll(0), a[i] - b[i - 1]);
		}
		for (int i = 1;i <= n;i++)
		{
			if (i == 1)ans = a[i] + sum - max(ll(0), a[i] - b[n]);
			else ans = min(ans, a[i] + sum - max(ll(0), a[i] - b[i - 1]));
		}
		cout << ans << endl;
	}
}
<think>根据报错信息,错误发生在 `GameManager.ts` 的第1748行(在 `checkSkillConditions` 方法中),具体是调用 `Vec3.distance` 时,其中一个参数为 `null`,无法读取 `x` 属性。 在 `checkSkillConditions` 方法中,我们有两处使用了 `Vec3.distance`: 1. 在检测万剑诀触发条件时:`const distance = Vec3.distance(this.HeroNode.position, this.lockedMonster.position);` 2. 在检测渡劫术触发条件时:`const distance = Vec3.distance(this.HeroNode.position, monster.position);` 错误很可能是由于 `this.lockedMonster` 或 `monster` 为 `null` 或 `undefined`。 解决方法:在使用 `Vec3.distance` 之前,先检查参数的有效性。 具体修改如下: 1. 对于万剑诀触发条件检测部分: ```typescript if (this.hasWanJianJue && this.lockedMonster && !this.chestSpawned) { // 确保lockedMonster和它的position存在 if (this.lockedMonster && this.lockedMonster.isValid && this.lockedMonster.position) { const distance = Vec3.distance(this.HeroNode.position, this.lockedMonster.position); // ... 其他代码 } } ``` 2. 对于渡劫术触发条件检测部分: ```typescript if (this.hasDuJieShu && this.duJieShuTimer >= this.duJieShuCooldown && this.chestSpawned === false) { const monsters = this.getAllMonsters(); const inRangeMonsters = monsters.filter(monster => { // 确保monster和它的position存在 if (monster && monster.isValid && monster.position) { const distance = Vec3.distance(this.HeroNode.position, monster.position); return distance <= 400; } return false; }); // ... 其他代码 } ``` 但是,请注意,在渡劫术部分,我们已经在 `getAllMonsters` 中过滤了无效节点和死亡的怪物,所以正常情况下 `monster` 和 `monster.position` 应该存在。但是为了安全,还是加上判断。 另外,在万剑诀部分,我们使用了 `this.lockedMonster`,这个变量可能在某个时刻被置为 `null`(比如当锁定的怪物死亡时)。因此,在访问 `this.lockedMonster.position` 之前,必须检查 `this.lockedMonster` 是否有效。 我们可以在 `GameManager` 中增加一个方法来安全地获取锁定的怪物,并在使用前调用。但为了快速修复,我们直接添加条件判断。 修改 `checkSkillConditions` 方法中的两段代码: 第一段(万剑诀): ```typescript // 检测万剑诀触发条件:锁定怪兽存在且在350像素范围内 if (this.hasWanJianJue && this.lockedMonster && !this.chestSpawned) { // 确保锁定的怪物节点有效且位置存在 if (this.lockedMonster.isValid && this.lockedMonster.position) { const distance = Vec3.distance( this.HeroNode.position, this.lockedMonster.position ); // 如果怪兽在350像素范围内 if (distance <= 350) { // ... 原有逻辑 } else { // 如果怪兽超出范围,重置万剑诀状态 this.wanJianJueActive = false; } } else { // 如果锁定的怪物无效,则重置万剑诀状态 this.wanJianJueActive = false; } } ``` 第二段(渡劫术): ```typescript // 检测渡劫术触发条件:与怪物距离在400以内,每0-1秒触发一次 if (this.hasDuJieShu && this.duJieShuTimer >= this.duJieShuCooldown && this.chestSpawned === false) { const monsters = this.getAllMonsters(); const inRangeMonsters = monsters.filter(monster => { // 确保怪物节点有效且位置存在 if (monster && monster.isValid && monster.position) { const distance = Vec3.distance(this.HeroNode.position, monster.position); return distance <= 400; } return false; }); if (inRangeMonsters.length > 0) { this.skillManager?.buttonDuJieShu(); this.duJieShuTimer = 0; this.duJieShuCooldown = 0 + Math.random(); // 重置冷却时间 AudioMgr.inst.playOneShot("audio/dujie",1); } } ``` 此外,我们还需要注意在其他地方(比如当锁定的怪物死亡时)将 `this.lockedMonster` 置为 `null`,以避免引用已销毁的节点。 例如,在怪物死亡的回调中,如果当前锁定的怪物就是死亡的怪物,则解除锁定: ```typescript // 在 MonsterController 的死亡处理中,通知 GameManager 有怪物死亡 // 然后在 GameManager 中,检查死亡的怪物是否是锁定的怪物 public onMonsterDied(monsterNode: Node) { if (this.lockedMonster === monsterNode) { this.lockedMonster = null; // 同时结束战斗状态 this.endBattle(); } // ... 其他处理(比如增加经验值) } ``` 由于代码中没有直接看到这样的回调,因此需要添加。如果还没有,建议在怪物死亡时触发一个事件,由 `GameManager` 来清理锁定。 但是,由于时间关系,我们先在 `checkSkillConditions` 中通过有效性检查来避免错误。 修改后的 `checkSkillConditions` 方法如下(只修改两处):</think>这个错误表明在计算两个点之间的距离时,其中一个点是 `null`。具体发生在 `GameManager` 的 `checkSkillConditions` 方法中(第1748行),当调用 `Vec3.distance()` 时,其中一个参数为 `null`。 ### 问题分析 错误发生在以下代码段: ```typescript const distance = Vec3.distance(this.HeroNode.position, monster.position); ``` 可能的原因: 1. `monster` 节点已被销毁但未从数组中移除 2. `monster` 节点被意外设置为 `null` 3. 在遍历过程中某些怪物节点无效 ### 解决方案 需要添加对节点有效性的检查。以下是修复步骤: #### 1. 修改 `getAllMonsters()` 方法 确保只返回有效且未销毁的怪物节点: ```typescript public getAllMonsters(): Node[] { return this.MonstersGroup.filter(monster => monster && monster.isValid && !monster.isDestroyed ); } ``` #### 2. 修改 `checkSkillConditions()` 方法 添加节点有效性检查: ```typescript private checkSkillConditions() { // ... 其他代码 ... // 检测渡劫术触发条件 if (this.hasDuJieShu && this.duJieShuTimer >= this.duJieShuCooldown && this.chestSpawned === false) { const monsters = this.getAllMonsters(); const inRangeMonsters = monsters.filter(monster => { // +++ 添加有效性检查 +++ if (!monster || !monster.isValid || monster.isDestroyed) return false; const distance = Vec3.distance(this.HeroNode.position, monster.position); return distance <= 400; }); // ... 后续逻辑 ... } } ``` #### 3. 修改 `cleanInvalidMonsters()` 方法 确保及时清理无效节点: ```typescript private cleanInvalidMonsters() { for (let i = this.MonstersGroup.length - 1; i >= 0; i--) { const monster = this.MonstersGroup[i]; if (!monster || !monster.isValid || monster.isDestroyed) { this.MonstersGroup.splice(i, 1); } } } ``` ### 完整修复代码 以下是修改后的相关方法: ```typescript // GameManager.ts private cleanInvalidMonsters() { for (let i = this.MonstersGroup.length - 1; i >= 0; i--) { const monster = this.MonstersGroup[i]; if (!monster || !monster.isValid || monster.isDestroyed) { this.MonstersGroup.splice(i, 1); } } } public getAllMonsters(): Node[] { return this.MonstersGroup.filter(monster => monster && monster.isValid && !monster.isDestroyed ); } private checkSkillConditions() { // 如果英雄死亡,不再触发任何技能 if (this.isHeroDied) return; // 检测万剑诀触发条件:锁定怪兽存在且在350像素范围内 if (this.hasWanJianJue && this.lockedMonster && !this.chestSpawned) { // 添加有效性检查 if (this.lockedMonster.isValid && !this.lockedMonster.isDestroyed) { const distance = Vec3.distance( this.HeroNode.position, this.lockedMonster.position ); // ... 原有逻辑 ... } } // 检测渡劫术触发条件:与怪物距离在400以内,每0-1秒触发一次 if (this.hasDuJieShu && this.duJieShuTimer >= this.duJieShuCooldown && this.chestSpawned === false) { const monsters = this.getAllMonsters(); const inRangeMonsters = monsters.filter(monster => { // 添加有效性检查 if (!monster || !monster.isValid || monster.isDestroyed) return false; const distance = Vec3.distance(this.HeroNode.position, monster.position); return distance <= 400; }); // ... 原有逻辑 ... } } ``` ### 额外建议 1. **防御性编程**:在所有使用 `Node.position` 的地方添加有效性检查 2. **定期清理**:在 `update()` 方法中增加清理频率: ```typescript update(deltaTime: number) { // 每帧都清理无效节点 this.cleanInvalidMonsters(); // ... 其他代码 ... } ``` 3. **使用安全访问**:考虑使用可选链操作符: ```typescript const pos = monster?.position; if (pos) { // 计算距离 } ``` 这些修改将确保在计算距离前节点是有效的,避免出现 "Cannot read properties of null" 的错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Rikka_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值