1. 程式人生 > 其它 >數列 - Sequence - Indexed Tree

數列 - 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;
		}
	}
}