一 问题描述
给定 N 个整数的非递减序列a1、a2、...an,对每个索引 i 和 j 组成的查询,都确定整数 ai...aj 中的最频繁值(出现次数最多的值)。
二 输入和输出
1 输入
包含多个测试用例,每个测试用例都以两个整数 n 和 q 的行开始。下一行包括 n 个整数 a1...an。对每个 I,都满足 ai<=ai+1。以下 q 行,每行都包含一个查询,有两个整数 i 和 j 组成,表示查询边界索引。在最后一个测试用例后跟一个包含单个 0 的行。
2 输出
对每个查询,都单含输出一个整数,表示给定范围内最频繁值出现的次数
三 输入和输出样例
1 输入样例
10 3
-1 -1 1 1 1 1 3 10 10 10
2 3
1 10
5 10
0
2 输出样例
1
4
3
四 分析
由于本问题可以将元素的出现次数累计,然后进行区间最值查询,所以可以使用 ST 解决。为提高求 log 的效率,首先用动态规划求出数据范围内所有数的 log 值,将其存储在数组 lb[] 中。使用时查询即可。F[i][j] 表示 [i + 2^j-1] 区间的最大值,区间长度为 2^j。
五 算法设计
1 求出数据范围内所有数的 log 值,将其存储在数组 lb[] 中.
2 非递减序列相等元素一定相邻,将每个元素和前面元素进行比较,将重复次数累计并存入 F[i][0] 中。
3 创建 ST。
4 查询[l,r]区间的最大值。若第 l 个数和前一个数相等,则首先统计第 l 个数在查询区间[l,r]的出现次数,再查询剩余区间的最大值,两者再求最大值。
六 图解
1 求出数据范围内所有数的 log 值,将其存储在数组 lb[] 中。
log[1]=log[0]+1=0
log[2]=log[1]+1=1
log[3]=log[2]=1
log[4]=log[3]+1=2
log[5]=log[4]=2
log[6]=log[5]=2
log[7]=log[6]=2
log[8]=log[7]+1=3
2 将输入样例元素的出现次数累计并存入F[i][0] 中。
3 创建 ST
4 查询 2 3
查询[2,3]区间最频繁值的出现次数。首先 t=l=2,因为 a[2]=a[1],t++,即 t=3;此时 a[3]<>a[2],t-l=1,RMQ(t,r)=RMQ(3,3)=1,求两者的最大值,得到 [2,3]区间最频繁值的出现次数为 1。
5 查询 1 10
查询[1,10]区间最频繁值的出现次数。首先,t=l=1,a[1]<>a[0],t-l=0,RMQ(t,r)=RMQ(1,10)=4,求两者的最大值,得到[1,10]区间最频繁值的出现次数为 4。
6 查询 5 10
查询[5,10]区间最频繁值的出现次数。首先,t=l=5,因为a[5]=a[4],t++,即 t=6;a[6=a[5],t++,即 t=7;此时 a[7]<>a[6],t-l=2,RMQ(t,r)=RMQ(7,10)=3,求两者的最大值,得到[5,10]区间最频繁值的出现次数为 3。
七 代码
package com.platform.modules.alg.alglib.poj3368;
public class Poj3368 {
public String output = "";
private final int maxn = 100010;
int a[] = new int[maxn]; // 数据
int lb[] = new int[maxn]; // 存储 log 值
int F[][] = new int[maxn][20]; // F(i,j) 表示区间 [i,i+2^j-1] 的最值,区间长度为 2^j
void Initlog() {//求解所有log值,保存到数组lb[]
lb[0] = -1;
for (int i = 1; i < maxn; i++)
lb[i] = (i & (i - 1)) > 0 ? lb[i - 1] : lb[i - 1] + 1;
}
void ST_create(int n) {//每个测试用例n不同,因此做参数
for (int j = 1; j <= lb[n]; j++)
for (int i = 1; i <= n - (1 << j) + 1; i++)//n-2^j+1
F[i][j] = Math.max(F[i][j - 1], F[i + (1 << (j - 1))][j - 1]);
}
int RMQ(int l, int r) {//求区间[l..r]的最值差
if (l > r) return 0;
int k = lb[r - l + 1];
return Math.max(F[l][k], F[r - (1 << k) + 1][k]);
}
public String cal(String input) {
int n, q, l, r;
Initlog();
String[] line = input.split("\n");
String[] words = line[0].split(" ");
n = Integer.parseInt(words[0]);
q = Integer.parseInt(words[1]);
String[] nums = line[1].split(" ");
for (int i = 1; i <= n; i++) { // 下标从1开始
a[i] = Integer.parseInt(nums[i - 1]);
if (i == 1) {
F[i][0] = 1;
continue;
}
if (a[i] == a[i - 1])
F[i][0] = F[i - 1][0] + 1;
else
F[i][0] = 1;
}
ST_create(n);
for (int j = 1; j <= q; j++) {
String[] query = line[j + 1].split(" ");
l = Integer.parseInt(query[0]);
r = Integer.parseInt(query[1]);
int t = l;
while (t <= r && a[t] == a[t - 1])
t++;
output += Math.max(t - l, RMQ(t, r)) + "\n";
}
return output;
}
}