单调队列 | 线段树 | 一维RMQ —— POJ 2823

对应POJ题目:点击打开链接

Sliding Window
Time Limit: 12000MS Memory Limit: 65536K
Total Submissions: 42356 Accepted: 12503
Case Time Limit: 5000MS

Description

An array of size  n ≤ 10 6 is given to you. There is a sliding window of size  k which is moving from the very left of the array to the very right. You can only see the  k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example: 
The array is  [1 3 -1 -3 5 3 6 7], and  k is 3.
Window positionMinimum valueMaximum value
[1  3  -1] -3  5  3  6  7 -13
 1 [3  -1  -3] 5  3  6  7 -33
 1  3 [-1  -3  5] 3  6  7 -35
 1  3  -1 [-3  5  3] 6  7 -35
 1  3  -1  -3 [5  3  6] 7 36
 1  3  -1  -3  5 [3  6  7]37

Your task is to determine the maximum and minimum values in the sliding window at each position. 

Input

The input consists of two lines. The first line contains two integers  n and  k which are the lengths of the array and the sliding window. There are  n integers in the second line. 

Output

There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values. 

Sample Input

8 3
1 3 -1 -3 5 3 6 7

Sample Output

-1 -3 -3 -3 3 3
3 3 5 5 6 7

题意:给出n个数,求从左往右每k个数的最值。


思路1:单调队列

        可在队首和队尾删除元素,在队尾插入元素。以递增队列(即队首元素最小,队列严格递增)为例,队尾删除元素:从队尾往前扫,一直pop掉不比e小的元素,e入队;队首删除元素:当队尾元素跟队首元素在原数组里的距离大于k时,就删除队首元素。

如本例:1  3  -1  -3  5  3  6  7

                0  1  2   3   4  5  6  7

1、(1,0)

2、(1,0)(3,1)

3、(-1,2)

4、(-3,3)

5、(-3,3)(5,4)

6、(-3,3)(3,5)

7、(3,5)(6,6)//这里在加入(6,6)时把(-3,3)pop掉是因为6和-3这两个元素的距离len = 4 > k

8、(3,5)(6,6,)(7,7)

由于每一次入队都保证了队首元素为最小值,所以从第k次开始,每一次的队首元素就是样例输出的最小值。最大值求法类似。


思路2:线段树。就是线段树的区间最值


思路3:RMQ。

使用二维RMQ肯定超内存。由于题目区间长度固定,所以可以用滚动数组的方法,所以使用一维就可以了。可以有两种表示方法:

1)dp[i]表示从下标i开始数的长度为2^m个数的最值,其中2^m 为小于k的最大值。

2)dp[i]表示代替原来的dp[i][j](dp[i][j]表示从下标i开始数的长度为2^j个数的最值,其中2^j 为小于k的最大值。


思路4:堆~(这部分知识待补)


优化:听说可以在I/O流中做文章,即缓存输入与无缓存输入(这部分知识待补


单调队列:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 1000005

int a[M];
int que[M];
int front, rear;
int p[M];
int fp, rp;

#define Front          que[front]
#define Rear           que[rear-1]
#define Push(x, i)     que[rear++] = x, p[rp++] = i
#define Pop            front++, fp++
#define Pop_back       rear--, rp--
#define Clearque       front = rear = fp = rp = 0
#define Lengthque      p[rp-1] - p[fp] + 1
#define Emptyque       (front == rear ? 1 : 0)

void SolveMin(int *A, int n, int k)
{
	int i;
	front = rear = fp = rp = 0;
	for(i = 0; i < n; i++){
		if(Emptyque && A[i] < Front) Clearque;
		else while(Rear >= A[i] && !Emptyque) Pop_back;
		Push(A[i], i);
		if(Lengthque > k) Pop;
		if(i >= k - 1){
			printf("%d", Front);
			if(i < n - 1) printf(" ");
		}
	}
	printf("\n");
}

void SolveMax(int *A, int n, int k)
{
	int i;
	front = rear = fp = rp = 0;
	for(i = 0; i < n; i++){
		if(Emptyque && A[i] > Front) Clearque;
		else while(Rear <= A[i] && !Emptyque) Pop_back;
		Push(A[i], i);
		if(Lengthque > k) Pop;
		if(i >= k - 1){
			printf("%d", Front);
			if(i < n - 1) printf(" ");
		}
	}
	printf("\n");
}

int main()
{
	//freopen("in.txt", "r", stdin);
	int n, k;
	int i, j;
	while(~scanf("%d%d", &n, &k))
	{
		for(i = 0; i < n; i++)
			scanf("%d", &a[i]);
		SolveMin(a, n, k);
		SolveMax(a, n, k);
	}
}

线段树:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 1000005
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))

int a[M];
int Max[M<<2];
int Min[M<<2];
int B[M];
int S[M];

typedef struct
{
	int b, s;
}Point;

void Build(int root, int left, int right)
{
	if(left == right - 1){
		Max[root] = Min[root] = a[left];
		return;
	}
	int mid = (left + right)>>1;
	Build(root<<1, left, mid);
	Build(root<<1|1, mid, right);
	Max[root] = MAX(Max[root<<1], Max[root<<1|1]);
	Min[root] = MIN(Min[root<<1], Min[root<<1|1]);
}

Point Query(int root , int left, int right, int l, int r)
{
	Point P, P1;
	if(l <= left && right <= r){
		P.b = Max[root];
		P.s = Min[root];
		return P;	
	}
	int mid = (left + right)>>1;
	int b1, b2;
	if(r <= mid) return Query(root<<1, left, mid, l, r);
	else if(l >= mid) return Query(root<<1|1, mid, right, l, r);
	else{
		P = Query(root<<1, left, mid, l, mid);
		P1 = Query(root<<1|1, mid, right, mid, r);
		Point p;
		p.b = MAX(P.b, P1.b);
		p.s = MIN(P.s, P1.s);
		return p;
	}
}

int main()
{
	//freopen("in.txt", "r", stdin);
	int n, k, u;
	int i;
	while(~scanf("%d%d", &n, &k))
	{
		for(i = 0; i < n; i++)
			scanf("%d", &a[i]);
		Build(1, 0, n);
		u = 0;
		Point P;
		for(i = 0; i + k <= n; i++){
			P = Query(1, 0, n, i, i + k);
			B[u] = P.b;
			S[u++] = P.s;
		}

		printf("%d", S[0]);
		for(i = 1; i < u; i++)
			printf(" %d", S[i]);
		printf("\n");

		printf("%d", B[0]);
		for(i = 1; i < u; i++)
			printf(" %d", B[i]);
		printf("\n");
	}
}

一维RMQ(1):

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define M 1010000
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))

int Max[M];
int Min[M];

int main()
{
	//freopen("in.txt", "r", stdin);
	int n, k, val;
	int i, j, limit;
	while(~scanf("%d%d", &n, &k))
	{
		for(i = 0; i < n; i++){
			scanf("%d", &val);
			Max[i] = Min[i] = val;
		}

		for(j = 1; (j<<1) < k; j <<= 1)
			for(i = 0; i + (j<<1) - 1 < n; i++){
				Max[i] = MAX(Max[i], Max[i+j]);
				Min[i] = MIN(Min[i], Min[i+j]);
			}

		printf("%d", MIN(Min[0], Min[k-j]));
		for(i = 1; i + k -1 < n; i++){
			printf(" %d", MIN(Min[i], Min[i+k-j]));
		}
		printf("\n");

		printf("%d", MAX(Max[0], Max[k-j]));
		for(i = 1; i + k -1 < n; i++){
			printf(" %d", MAX(Max[i], Max[i+k-j]));
		}
		printf("\n");
	}
}

一维RMQ(2):
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define M 1000005
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))

int a[M];
int Max[M];
int Min[M];

void caldp(int n, int limit)  
{  
	int i, j;
	int r1, r2;

    for(i=0; i<n; i++)
        Max[i] = Min[i] = a[i]; 

    for(j=1; j<=limit; j++){ //注意这个要在外层
        for(i=0; i<n; i++){
            if(i+(1<<j)-1 < n){
                //Max[i][j] = MAX(Max[i][j-1], Max[i+(1<<(j-1))][j-1]);  
                //Min[i][j] = MIN(Min[i][j-1], Min[i+(1<<(j-1))][j-1]);  
                Max[i] = MAX(Max[i], Max[i+(1<<(j-1))]);  
                Min[i] = MIN(Min[i], Min[i+(1<<(j-1))]);  
            }  
        }  
    }  
}  
  
int rmq_max(int limit, int l, int r)  
{  
    return MAX(Max[l], Max[r-(1<<limit)+1]);
}  

int rmq_min(int limit, int l, int r)  
{  
    return MIN(Min[l], Min[r-(1<<limit)+1]);  
}  

int main()
{
	//freopen("in.txt", "r", stdin);
	int n, k, u;
	int i, limit;
	while(~scanf("%d%d", &n, &k))
	{
		for(i = 0; i < n; i++)
			scanf("%d", &a[i]);

		limit = log(double(k)) / log(2.0);
		caldp(n, limit);  

		printf("%d", rmq_min(limit, 0, k - 1));
		for(i = 1; i + k <= n; i++){
			printf(" %d", rmq_min(limit, i, i + k - 1));
		}
		printf("\n");

		printf("%d", rmq_max(limit, 0, k - 1));
		for(i = 1; i + k <= n; i++){
			printf(" %d", rmq_max(limit, i, i + k - 1));
		}
		printf("\n");
	}
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值