【JAVA】最大间隔限定的最大和(Pro20210827)(DP(IndexTree / PriorityQueue)

【JAVA】最大间隔限定的最大和(Pro20210827)(DP(IndexTree / PriorityQueue)

题目

Lia 在一个景点看到了由 N 个垫脚石组成的石桥。单纯地走过这些垫脚石感觉没有意思,所以她给每块垫脚石设置了一个分数,将踩到的所有石头上的分数全部加起来,想要得到一个最大分数。
但是,为了让这件事更有意思,她决定按下列规则来过桥。

  1. 第一块和最后一块垫脚石必须要踩。
  2. 设定一个K值,下一块踩的石头与上一块踩的石头间相隔的距离最大为 K。
  3. 不能重复踩同一块垫脚石。

假设 N 是 7,每个石头的分数如上图所示。如果 K 是 2,按 ① → ② → ④ → ⑥ → ⑦ 的方式移动可以获得最高分数,分数是 1 (-6 + -4 + 4 + -2 + 9)。如果 K 是 3,按 ① → ④ → ⑦ 的方式移动可以获得最高分数,分数是 7 (-6 + 4 + 9)。

请帮助 Lia 计算出按规则踩着垫脚石过桥时,可以获得的最高分数。

[限制条件]

  1. 组成桥的垫脚石数量 N 为介于 1 到 300,000 之间的整数。
  2. 每个垫脚石的分数在带符号的 32 位整数值范围内。
  3. K 为介于 1 到 N 之间的整数。

[输入]
首先,给定测试用例数量 T,后面接着输入 T 种测试用例。在每个测试用例的第一行,给定垫脚石数量 N 和距离 K,以空格分隔。在下一行,给定从 1 到 N 号垫脚石每个的分数,以空格分隔。

[输出]
每行输出一个测试用例。首先,输出“#x”(其中 x 表示测试用例编号,从 1 开始)加上一个空格,然后输出问题中要求的值。

[输入和输出示例]
(输入)
3
7 2
-6 -4 -6 4 -4 -2 9
7 3
-6 -4 -6 4 -4 -2 9
10 2
-3 -4 -2 -1 -7 -5 -9 -4 -7 -5

(输出)
#1 1
#2 7
#3 -20

思路

对于某个石头n,可以跳到这个石头的石头的下标,一定在区间 [n - M, n - 1] 范围内。
即:f(n) = data[n] + Max(f(n-M), f(n-M+1), f(n-M+2), …, f(n-1))

Max(f(n-M), f(n-M+1), f(n-M+2), …, f(n-1)) 即为连续区间最大值,可以使用 IndexTree 或 PriorityQueue。

代码(IndexTree)

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
	static int T, N, M;
	static long ASW[];

	public static void main(String[] args) throws Exception {
		System.setIn(new FileInputStream("D:\\SW\\TestCase\\sample_input_20210827.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int T = Integer.parseInt(br.readLine());

		for (int tc = 1; tc <= T; tc++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			N = Integer.parseInt(st.nextToken()); // 石头数量
			M = Integer.parseInt(st.nextToken()); // 跳跃间隔

			int idx = 1; // Tree第一个叶节点下标
			while (idx < N)
				idx *= 2;

			ASW = new long[2 * idx]; // Index Tree
			Arrays.fill(ASW, Long.MIN_VALUE);

			st = new StringTokenizer(br.readLine());

			// 第一个石头必须踩
			long score = Long.parseLong(st.nextToken());
			update(idx, score);

			// f(n) = data[n] + Max(f(n-M), f(n-M+1), f(n-M+2), ..., f(n-1))
			for (int s, e, i = 1; i < N; i++) {
				s = idx + Math.max(i - M, 0); // 必须确保s在叶节点层(即:s >= idx)
				e = i + idx - 1;

				score = query(s, e) + Long.parseLong(st.nextToken());
				update(i + idx, score);
			}

			System.out.printf("#%d %d\n", tc, score);
		}
	}

	private static long query(int sIdx, int eIdx) {
		long result = Long.MIN_VALUE;

		while (sIdx <= eIdx) {
			if (sIdx % 2 > 0)
				result = Math.max(result, ASW[sIdx]);

			if (eIdx % 2 == 0)
				result = Math.max(result, ASW[eIdx]);

			sIdx = (sIdx + 1) / 2;
			eIdx = (eIdx - 1) / 2;
		}

		return result;
	}

	private static void update(int idx, long value) {
		while (idx > 0) {
			ASW[idx] = Math.max(value, ASW[idx]);
			idx /= 2;
		}
	}
}

代码(PriorityQueue)

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {
	static int T, N, M, ASW;
	static PriorityQueue<Stone> pq = new PriorityQueue<Stone>((o1, o2) -> (o2.score - o1.score) > 0 ? 1 : -1);

	public static void main(String[] args) throws Exception {
		System.setIn(new FileInputStream("D:\\SW\\TestCase\\sample_input_20210827.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int T = Integer.parseInt(br.readLine());

		for (int tc = 1; tc <= T; tc++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			N = Integer.parseInt(st.nextToken()); // 石头数量
			M = Integer.parseInt(st.nextToken()); // 跳跃间隔
			pq.clear();

			st = new StringTokenizer(br.readLine());

			// 第一个石头必须踩
			long score = Long.parseLong(st.nextToken());
			pq.add(new Stone(1, score));

			// f(n) = data[n] + Max(f(n-M), f(n-M+1), f(n-M+2), ..., f(n-1))
			for (int i = 2; i <= N; i++) {
				score = getMax(i).score + Long.parseLong(st.nextToken());

				pq.add(new Stone(i, score));
			}

			System.out.printf("#%d %d\n", tc, score);
		}
	}

	/**
	 * @param i 当前是第几块石头
	 * @return 在间隔范围内的最大分值的石头(可以跳到当前石头上的石头中,最大分值的石头)
	 */
	private static Stone getMax(int i) {
		while (pq.peek().idx < i - M) // 不能间隔大于M的,略去
			pq.poll();

		return pq.peek();
	}
}

class Stone {
	int idx;
	long score;

	public Stone(int idx, long score) {
		this.idx = idx;
		this.score = score;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PriorityQueue 是一种数据结构,它可以用来实现最大堆。最大堆是一种特殊的二叉树,其中每个节点的值都大于或等于其子节点的值。 在 Java 中,PriorityQueue 类默认实现的是最小堆,但我们可以通过指定自定义的比较器来实现最大堆。比较器可以按照我们的需求来定义元素之间的比较规则。 以下是一个使用 PriorityQueue 实现最大堆的示例代码: ``` import java.util.Comparator; import java.util.PriorityQueue; public class MaxHeapExample { public static void main(String[] args) { // 创建一个自定义比较器,用于实现最大堆 Comparator<Integer> maxHeapComparator = Comparator.reverseOrder(); // 创建一个最大PriorityQueue PriorityQueue<Integer> maxHeap = new PriorityQueue<>(maxHeapComparator); // 向最大堆中添加元素 maxHeap.add(5); maxHeap.add(3); maxHeap.add(8); maxHeap.add(1); maxHeap.add(10); // 输出最大堆中的元素 while (!maxHeap.isEmpty()) { System.out.println(maxHeap.poll()); } } } ``` 在上面的示例中,我们首先创建了一个自定义的比较器 `maxHeapComparator`,它将元素按照降序排列。然后,我们使用这个比较器来创建一个 PriorityQueue 对象 `maxHeap`,从而实现了最大堆。 接下来,我们向最大堆中添加了一些元素,并使用 `poll()` 方法逐个取出并输出最大堆中的元素。由于最大堆的特性,输出结果会按照降序排列。 希望这个示例能够帮助你理解如何使用 PriorityQueue 实现最大堆。如果你有任何疑问,请随时提问!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值