LeetCode-问题2106-摘水果

题目2106要求在最多k步内从startPos出发收割最多水果。初始尝试的BFS暴力求解因路径干扰失败。通过分析示例,发现最优解在于预处理startPos左右两侧的最远可达点的累加水果数。总结了解题思路。
摘要由CSDN通过智能技术生成

题目链接

https://leetcode.cn/problems/maximum-fruits-harvested-after-at-most-k-steps/

解答过程

题目要求并不难理解,从某个startPos开始,每一步可以向左走或者向右走,最多可以走k步,凡是经过的点的水果就可以收割,求最多能收割多少水果。

一开始我的想法是用类似宽度优先遍历BFS的方式去暴力计算,模拟k步,每一步计算当前可能到达的点是哪些,以及每一个可能到达的点的累加水果数,最终取走完k步后能到达的点中累加水果数最大的那一个。乍一想好像还挺有道理,草草写了一版后验证,果然不行。稍作分析,可以发现,上面的思路中在首次访问到某个点时就会把该点上的水果收割,而其他路径再走到这个点时已经没有水果可收割,即不同路径之间有干扰。换句话说,要想用类似的暴力方式计算,需要维护每一条路径,并累加路径上的水果数,最终得到水果数最大的路径。由于路径数是指数级上升的,并不可取。

没有什么思路的情况下,我一般会从示例问题入手,尝试基于具体的、小规模的问题推理解法。比如示例问题中从startPos=5开始走4步,那么显然,你有以下这些选择:

  • 往左走4步,此时位于点1
  • 往左走3步,再往右1步,此时位于点3
  • 往左走2步,再往右2步,此时回到点5
  • 往左走1步,再往右3步,此时位于点7
  • 往右走1步,再往左3步,此时位于点3
  • 往右走2步,再往左2步,此时回到点5
  • 往右走3步,再往左1步,此时位于点7
  • 往右走4步,此时位于点9

发现规律了吗?走k步可以做类似拆分,而每一种走法下能收割的水果数由以下三部分组成:

  • startPos点的水果数
  • startPos以左能到达的最远点内累加的水果数
  • startPos以右能到达的最远点内累加的水果数

startPos以左能到达的每个点内累加的水果数恰恰是可以预加工出来的,startPos以右能到达的每个点内累加的水果数也是一样可以预加工出来的。

	public int maxTotalFruits(int[][] fruits, int startPos, int k) {
		int length = fruits.length;
		int maxIdx = Math.max(fruits[length - 1][0], startPos);
		int[] fruitArr = new int[maxIdx + 1];
		for (int i = 0; i < length; i++) {
			fruitArr[fruits[i][0]] = fruits[i][1];
		}

		if (k == 0) {
			return fruitArr[startPos];
		}

		// left[i] represents harvested fruits number when go left i step, startPos not included
		int maxLeftSteps = Math.min(startPos, k);
		int[] leftFruits = new int[maxLeftSteps + 1];
		for (int i = 1; i < leftFruits.length; i++) {
			leftFruits[i] = leftFruits[i-1] + fruitArr[startPos - i];
		}

		// right[i] represents harvested fruits number when go right i step, startPos not included
		int maxRightSteps = Math.min(maxIdx - startPos, k);
		int[] rightFruits = new int[maxRightSteps + 1];
		for (int i = 1; i < rightFruits.length; i++) {
			rightFruits[i] = rightFruits[i-1] + fruitArr[startPos + i];
		}

		int maxFruits = 0;

		int startPosFruits = fruitArr[startPos];

		for (int leftStep = 0; leftStep <= maxLeftSteps; leftStep++) {
			int leftStepFruits = leftFruits[leftStep];

			int rightStep = k - (leftStep * 2);
			if (rightStep <= 0) {
				rightStep = 0;
			} else if (rightStep > maxRightSteps) {
				rightStep = maxRightSteps;
			}
			int rightStepFruits = rightFruits[rightStep];

			if (startPosFruits + leftStepFruits + rightStepFruits > maxFruits) {
				maxFruits = startPosFruits + leftStepFruits + rightStepFruits;
			}
		}

		for (int rightStep = 0; rightStep <= maxRightSteps; rightStep++) {
			int rightStepFruits = rightFruits[rightStep];

			int leftStep = k - (rightStep * 2);
			if (leftStep <= 0) {
				leftStep = 0;
			} else if (leftStep > maxLeftSteps) {
				leftStep = maxLeftSteps;
			}
			int leftStepFruits = leftFruits[leftStep];

			if (startPosFruits + leftStepFruits + rightStepFruits > maxFruits) {
				maxFruits = startPosFruits + leftStepFruits + rightStepFruits;
			}
		}

		return maxFruits;
	}

这个题目刚入手时,还是有点害怕的,因为很少做hard难度的题目。。。看了下官方题解,两种解法都似懂非懂,只能说官方题解更专业更有深度,但我觉得我的解法更好理解。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值