數列 - Sequence - Indexed Tree
數列
測試用例總數:40 個用例,1.5 秒 (C/C++),2 秒 (JAVA)
當前有一組包含N個數字的數列。當從數列中選取幾個連續的數字時,想在這些選擇的數字中建立最小值和最大值差為K的子數列。請求出最大值和最小值差為K的子數列中長度為最短的情況。
下面案例是從包含10個數的數列中找出K為10的子數列。
為方便起見,數列的第一個數字開始定義為 index 1, index 2, …, index 10
當選擇Index 1到5時,最大值為11,最小值為1,滿足差值為K的要求,並且長度為 5。當選擇Index 5到8時,最大值為21,最小值為11,滿足差值為K的要求,並且長度為4。這是滿足條件的最短的子數列。
給出陣列大小N和最大值與最小值之差K,以及陣列的值時,請找出可選擇的最短長度子數列和長度最短的子數列個數。如果不存在這種情況,則輸出 -1。
[限制條件]
1.數字個數N為介於1到100,000之間的整數。
2.各數值為介於1到1,000,000,000之間的整數。
3.最大值與最小值之差K為介於1到1,000,000,000之間的整數。
4.一個數列中的數值不存在重複的情況。
[輸入]
首先給出測試用例數量T,接著給出T種測試用例。每個測試用例的第一行空格區分給出數字個數N,以及最大值與最小值之差K。第二行空格區分給出N個數值。
[輸出]
每個測試用例輸出一行。各測試用例都輸出“#x”(其中x表示測試用例編號,從1開始),隔一個空格後,請輸出滿足最大值與最小值之差為K的子數列中,長度最短的子數列長度和長度最短的子數列的個數。如果沒有滿足最大值與最小值之差為K的子數列,則輸出 -1。
[輸入輸出 示例]
(輸入)
4
10 10
1 7 2 5 11 17 13 21 3 6
8 3
6 1 2 4 3 5 9 7
4 10
7 2 4 3
12 100
2 49 1 90 51 101 149 151 112 152 113 222
(輸出)
#1 4 1
#2 3 1
#3 -1
#4 4 2
思路分析:
要求區間最大值、最小值,首先考慮Indexed Tree
如果逐個去查尋最大值最小值並求差值,資料量為10W,效率肯定過不去,因此需要反向思維,即資料從小到大排序後,逐個找當前數+K的數(有序,可使用二分查詢)
如果當前數+K存在,則去原陣列中,找兩個數之間的最大值與最小值,如果最大值=當前數+K且最小值=當前數,則此區間是區滿足題目條件的區間
package tree; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.StringTokenizer; /** * 思路:要求區間最大值、最小值,首先考慮Indexed Tree * 如果逐個去查尋最大值最小值並求差值,資料量為10W,效率肯定過不去,因此需要反向思維,即資料從小到大排序後,逐個找當前數+K的數(有序,可使用二分查詢) * 如果當前數+K存在,則去原陣列中,找兩個數之間的最大值與最小值,如果最大值=當前數+K且最小值=當前數,則此區間是區滿足題目條件的區間 * @author XA-GDD * */ public class EQ_Sequence_0329 { static int T,N, K; static int _max_Nval = 100000; static Node []srcArr = new Node[_max_Nval]; static long[] maxIdxTree = new long[_max_Nval*4]; //最大值Indexed Tree static long [] minIdxTree = new long[_max_Nval*4]; //最小值Indexed Tree static int offset; static int seqLength; static int seqCnt; public static void main(String[] args) throws IOException { System.setIn(new FileInputStream("D:\\workspace\\sw_pro\\sample_input_0329.txt")); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); T = Integer.parseInt(st.nextToken()); for(int testCase = 1; testCase<=T;testCase++) { st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); K = Integer.parseInt(st.nextToken()); //初始化Tree int k=0; while((1<<k)<N) { k++; } offset = 1<<k; Arrays.fill(maxIdxTree, Long.MIN_VALUE); Arrays.fill(minIdxTree, Long.MAX_VALUE>>1); Arrays.fill(srcArr, null); seqLength = Integer.MAX_VALUE; seqCnt = 0; st = new StringTokenizer(br.readLine()); for(int i=0;i<N;i++) { srcArr[i] = new Node(i,Integer.parseInt(st.nextToken())); //將原陣列更新到Tree上 update(offset+i,srcArr[i].val); } //將原陣列按照從小到大排序 Arrays.sort(srcArr, 0, N); for(int i=0;i<N;i++) { //按照排序後的順序,使用二分查詢,逐個找當前數+K的數 int idx = Arrays.binarySearch(srcArr, 0, N, new Node(0,srcArr[i].val+K)); if(idx>0) { int left = (int) Math.min(srcArr[i].idx, srcArr[idx].idx); //兩個數在原陣列靠前的,在tree中的位置靠左 int right = (int) Math.max(srcArr[i].idx, srcArr[idx].idx); //兩個數在原陣列靠後的,在tree中的位置靠右 //如果最大值=當前數+K且最小值=當前數,則此區間是區滿足題目條件的區間 if(query(left+offset,right+offset,srcArr[i].val,srcArr[i].val+K)) { int length = right-left+1; if(length==seqLength) { seqCnt++; }else if(length<seqLength) { seqLength = length; seqCnt=1; } } } } if(seqCnt==0) { System.out.printf("#%d %d\n",testCase,-1); }else { System.out.printf("#%d %d %d\n",testCase,seqLength,seqCnt); } } } static void update(int index , long value) { maxIdxTree[index] = value; minIdxTree[index] = value; index = index >> 1; while(index>0) { maxIdxTree[index] = Math.max(maxIdxTree[index*2], maxIdxTree[index*2+1]); minIdxTree[index] = Math.min(minIdxTree[index*2], minIdxTree[index*2+1]); index = index >> 1; } } static boolean query(int start, int end, long leftVal, long rightVal) { long minVal = Long.MAX_VALUE>>1; long maxVal = Long.MIN_VALUE; while(start<=end) { if(start%2==1){ minVal = Math.min(minVal, minIdxTree[start]); maxVal = Math.max(maxVal, maxIdxTree[start]); } if(end%2==0){ minVal = Math.min(minVal, minIdxTree[end]); maxVal = Math.max(maxVal, maxIdxTree[end]); } start = (start+1)>>1; end = (end-1)>>1; } return minVal == leftVal && maxVal == rightVal; } static class Node implements Comparable<Node>{ int idx; int val; Node(int idx , int val){ this.idx = idx; this.val = val; } @Override public int compareTo(Node o) { return this.val - o.val; } } }