liuxuhelloworld's notebook

题目链接

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

解答过程

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

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

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

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

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难度的题目。。。看了下官方题解,两种解法都似懂非懂,只能说官方题解更专业更有深度,但我觉得我的解法更好理解。