一 问题描述
每天挤奶时,约翰的 N 头奶牛都以相同的顺序排队,他挑选一系列连续的奶牛来做游戏。为了让所有的奶牛都开心,他们的高度差异不应该太大。约翰列出的 Q 组奶牛和他们的高度,他希望确定每个小组中最高和最矮的奶牛之间的高度差异。
二 输入和输出
1 输入
第 1 行包含两个整数,N 和 Q,接下来的 N 行。每行包含一个整数,表示奶牛的高度,最后 Q 行,每行都包含两个整数 A 和 B,代表从 A 到 B 的奶牛范围。
2 输出
输出 Q 行,每行都包含一个整数,表示该范围内最高和最矮奶牛的高度差。
三 输入和输出样例
1 输入样例
6 3
1
7
3
4
2
5
1 5
4 6
2 2
2 输出样例
6
3
0
四 分析和设计
1 分析
本问题是典型的区间最值查询问题,可采用分块解决。
2 设计
a 分块
划分块,记录每个元素的所属块,每块的左右下标、最大值和最小值。
b 查询
查询区间[l, r] 区间最大值和最小值的差值
• 如果该区间属于同一块,则暴力统计最大值和最小值,返回两者差值;
• 如果该区间包含多块,则统计中间每一块的最大值和最小值,然后暴力统计左端和右端的最大值和最小值,返回两者差值。
五 代码
package com.platform.modules.alg.alglib.poj3264;
import static java.lang.Integer.max;
import static java.lang.Integer.min;
public class Poj3264Block {
public final int inf = 0x3f3f3f3f;
public final int maxn = 50010;
int L[] = new int[maxn];
int R[] = new int[maxn];
int belong[] = new int[maxn];
int block_max[] = new int[maxn];
int block_min[] = new int[maxn];
int a[] = new int[maxn];
int n;
int m;
public String output = "";
void build() {
int t = (int) Math.sqrt(n);
int num = n / t;
if (n % num != 0) num++;
for (int i = 1; i <= num; i++) {
L[i] = (i - 1) * t + 1;
R[i] = i * t;
}
R[num] = n;
for (int i = 1; i <= n; i++)
belong[i] = (i - 1) / t + 1;
// 求每块最值
for (int i = 1; i <= num; i++) {
int MIN = inf, MAX = -inf;
for (int j = L[i]; j <= R[i]; j++) {
MAX = max(MAX, a[j]);
MIN = min(MIN, a[j]);
}
block_max[i] = MAX;
block_min[i] = MIN;
}
}
int query(int l, int r) {
int MIN = inf, MAX = -inf;
if (belong[l] == belong[r]) {
for (int i = l; i <= r; i++) {
MAX = max(MAX, a[i]);
MIN = min(MIN, a[i]);
}
return MAX - MIN;
} else {
for (int i = l; i <= R[belong[l]]; i++) {//左端
MAX = max(MAX, a[i]);
MIN = min(MIN, a[i]);
}
for (int i = belong[l] + 1; i < belong[r]; i++) {//中间
MAX = max(MAX, block_max[i]);
MIN = min(MIN, block_min[i]);
}
for (int i = L[belong[r]]; i <= r; i++) {//右端
MAX = max(MAX, a[i]);
MIN = min(MIN, a[i]);
}
}
return MAX - MIN;
}
public String cal(String input) {
int l, r;
String[] line = input.split("\n");
String[] num = line[0].split(" ");
n = Integer.parseInt(num[0]);
m = Integer.parseInt(num[1]);
// 下标从1开始
for (int i = 1; i <= n; i++)
a[i] = Integer.parseInt(line[i]);
build();
for (int j = 1; j <= m; j++) {
String[] command = line[n + j].split(" ");
l = Integer.parseInt(command[0]);
r = Integer.parseInt(command[1]);
output += query(l, r) + "\n";
}
return output;
}
}
六 测试


265

被折叠的 条评论
为什么被折叠?



