题目链接:http://acm.ocrosoft.com/problem.php?cid=1615&pid=13

题目描述:

给你一个长度为N的数组,一个长为K的滑动窗体从最左端移至最右端,你只能看到窗口中的K个数,每次窗体向右移动一位,如下表所示:

 

 

     窗口位置              最小值     最大值

 [ 1 3 -1 ] -3 5 3 6 7       -1         3

 1 [ 3 -1 -3 ] 5 3 6 7       -3         3

 1 3 [ -1 -3 5 ] 3 6 7       -3         5

 1 3 -1 [ -3 5 3 ] 6 7       -3         5

 1 3 -1 -3 [ 5 3 6 ] 7        3         6

 1 3 -1 -3 5 [ 3 6 7 ]        3         7

 

你的任务是找出窗体在各个位置时的最大值和最小值。

输入

第一行:两个整数N和K

第二行:N个整数,表示数组的N个元素(<=2*10^9)。

输出

第一行为滑动窗口从左向右移动到每个位置时的最小值,每个数之间用一个空格分开。

第一行为滑动窗口从左向右移动到每个位置时的最大值,每个数之间用一个空格分开。

 

样例输入:

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

样例输出:

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

思路:

建一个dp[i][j],i表示从i的位置开始扫描,j表示从i开始(i也包括)往后扫描(1<<j)个数

然后直接RMQ算法,输出的时候优化一下就行了。

这题本身其实是用单调队列写的,想看单调队列的写法可以直接去这个网址,注释不想打了,就是完完全全没有加另外其他任何东西的单调队列。

单调队列AC代码:https://paste.ubuntu.com/p/vXdJJGQqQs/

dp+rmq算法代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int dp[1000005][30];//动态规划 ,dp[i][j],j的意思是1<<j(扫描的个数)!!  j的意思是1<<j(扫描的个数)!!  j的意思是1<<j(扫描的个数)!! 重要的事情说3遍!!! 
int a[1000005];//用于存储数字 
void st_max(int m)//初始化dp 
{
	for (int i = 1; i <= m; i++)
	{
		dp[i][0] = a[i];//j位置为0时,相当于1<<0=1,所以在范围为1的区间里的最值就是a[i] 
	}
	for (int j = 1; (1 << j) <= m; j++)
	{
		for (int i = 1; i + (1 << j) - 1 <= m; i++)
		{
			dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
			//这里不太好解释,就是比如i到i+(1<<j)-1个数分为2组,一组是i到 i+(1<<(j-1))-1,另一组是 i+(1<<(j-1))到i+(1<<j)-1,进行动态规划 
		}
	}
}
int rmq_max(int l, int r)//取l到r区间里的最值 
{
	int k = 0;
	while ((1 << (k + 1)) <= r - l + 1)k++;//找到某个k可以让2块长为(1<<k)的区间将l到r的区间完全覆盖 
	return max(dp[l][k], dp[r - (1 << k) + 1][k]);//取最值 
}
void st_min(int m)//这个和上诉一猫一样 
{
	for (int i = 1; i <= m; i++)
	{
		dp[i][0] = a[i];
	}
	for (int j = 1; (1 << j) <= m; j++)
	{
		for (int i = 1; i + (1 << j) - 1 <= m; i++)
		{
			dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
		}
	}
}
int rmq_min(int l, int r)
{
	int k = 0;
	while ((1 << (k + 1)) <= r - l + 1)k++;
	return min(dp[l][k], dp[r - (1 << (k)) + 1][k]);
}
int main()
{
	int n;
	scanf("%d", &n);
	int k;
	scanf("%d", &k);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i]);
	}
	st_min(n);
	//这里就是输出答案时的优化
	//假如你已经有了某个区间的最值,并且这个区间起点不是最值,那么下个区间的最值只需要用新加进来的元素和原来的最值比对,否则重新RMQ跑一遍 
	int minn = rmq_min(1, 1 + k - 1);// 有了第一个区间的最值
	printf("%d ", minn);
	for (int i = 2; i <= n - k + 1; i++)
	{
		if (a[i - 1] == minn)//假如这个区间的最值就是第一个数,那算后一个区间就要RMQ重新跑一遍 
		{
			printf("%d ",rmq_min(i, i + k - 1) );
			minn = rmq_min(i, i + k - 1);//重新记录最值 
		}
		else
		{
			minn = min(minn, a[i + k - 1]);// 新区间的最值只需要用新加进来的元素和原来的最值比对
			printf("%d ", minn);
		}
	}
	/*这里是TLE的输出代码,直接暴力RMQ会TLE
	for (int i = 1; i <= n - k + 1; i++)
	{
		cout << rmq_min(i, i + k - 1) << " ";
	}*/
	cout << endl;
	memset(dp, 0, sizeof(dp));
	st_max(n);
	//和上诉一毛一样 
	int maxx = rmq_max(1, 1 + k - 1);
	printf("%d ", maxx);
	for (int i = 2; i <= n - k + 1; i++)
	{
		if (a[i - 1] == maxx)
		{
			printf("%d ",rmq_max(i, i + k - 1) );
			maxx = rmq_max(i, i + k - 1);
		}
		else
		{
			maxx = max(maxx, a[i + k - 1]);
			printf("%d ", maxx);
		}
	}
	/*
	for (int i = 1; i <= n - k + 1; i++)
	{
		cout << rmq_max(i, i + k - 1) << " ";
	}*/
	return 0;
}

 

单调队列代码:

#include<iostream>
#include<deque>
#include<stdio.h>
using namespace std;
struct www
{
	int zhi;
	int pos;
}a[1000005];
int zeng1[1000005];
int jian1[1000005];
int lenzeng = 1;
int lenjian = 1;
int main()
{
	int n, k;
	cin >> n >> k;
	deque<www>zeng;
	deque<www>jian;
	int num = 1;
	for (int i = 1; i <= n; i++)
	{
		www x;
		scanf("%d", &x.zhi);
		x.pos = i;
		if (num < k)
		{
			num++;
			if (zeng.empty())
			{
				zeng.push_back(x);
			}
			else
			{
				while (zeng.back().zhi >= x.zhi)
				{
					zeng.pop_back();
					if (zeng.empty())
					{
						break;
					}
				}
				zeng.push_back(x);
			}
			if (jian.empty())
			{
				jian.push_back(x);
			}
			else
			{
				while (jian.back().zhi <= x.zhi)
				{
					jian.pop_back();
					if (jian.empty())
					{
						break;
					}
				}
				jian.push_back(x);
			}

		}
		else
		{
			if (i - zeng.front().pos == k)
			{
				zeng.pop_front();
				if (zeng.empty())
				{
					zeng.push_back(x);
				}
				else
				{
					while (zeng.back().zhi >= x.zhi)
					{
						zeng.pop_back();
						if (zeng.empty())
						{
							break;
						}
					}
					zeng.push_back(x);
				}
				zeng1[lenzeng++] = zeng.front().zhi;
			}
			else
			{
				while (zeng.back().zhi >= x.zhi)
				{
					zeng.pop_back();
					if (zeng.empty())
					{
						break;
					}
				}
				zeng.push_back(x);
				zeng1[lenzeng++] = zeng.front().zhi;
			}
			if (i -jian.front().pos == k)
			{
				jian.pop_front();
				if (jian.empty())
				{
					jian.push_back(x);
				}
				else
				{
					while (jian.back().zhi <= x.zhi)
					{
						jian.pop_back();
						if (jian.empty())
						{
							break;
						}
					}
					jian.push_back(x);
				}
				jian1[lenjian++] = jian.front().zhi;
			}
			else
			{
				while (jian.back().zhi <= x.zhi)
				{
					jian.pop_back();
					if (jian.empty())
					{
						break;
					}
				}
				jian.push_back(x);
				jian1[lenjian++] = jian.front().zhi;
			}
		}
	}
	for (int i = 1; i < lenzeng; i++)
	{
		printf("%d ", zeng1[i]);
	}
	printf("\n");
	for (int i = 1; i < lenjian; i++)
	{
		printf("%d ", jian1[i]);
	}
	return 0;
}