附近最小[蓝桥杯]

题目描述

在这里插入图片描述
在这里插入图片描述

方法一:暴力枚举(过80%数据)

思路

枚举每一个序号i所对应的范围[i - k ,i + k],不断更新最小值

代码

#include <iostream>
using namespace std;
const int N = 1000010;
int a[N];
int n, k; 
int main() 
{
	cin >> n;
	for(int i = 1; i <= n; i ++) cin >> a[i];
	cin >> k;
	for(int i = 1; i <= n; i ++)
	{
		int temp = N;
		for(int j = -k; j <= k; j ++)
		{
			if(i + j < 1 || i + j > n) continue;
			temp = min(temp, a[i + j]);
		}
		if(temp != N) cout << temp << " ";
	}
	return 0; 
}

方法二:单调队列

思路

基于方法一的思路与样例的结果,可以发现,不同序号对应的最小值是相同的,说明在一定范围内,最小值是不变的,因此可以用单调队列维护这样的结果

保证单调队列的首元素为最小值的下标,当一个元素>队列对应尾元素时,将其下标直接队列;否则不断删除队列尾元素,直至当前元素>尾元素

单调队列里存放的是下标!!!

前提知识:数组模拟队列

//q存放队列元素,hh存放队首下标,tt存放队尾下标
int q[N],hh,tt=-1;
//队尾插入
q[++tt]=x;
//弹出
hh++;
//判断队列是否为空
if(hh<=tt) not empty
else empty
//取出队首或队尾元素
q[tt],q[hh];

AC代码

#include <iostream>
using namespace std;
const int N = 1000010; 
int a[N];
int q[N]; //采用数组模拟双端队列,速度更快 
int n, k; 
int main() 
{
	cin >> n;
	for(int i = 1; i <= n; i ++) cin >> a[i];
	
	cin >> k;
	
	int hh = 0, tt = -1; //指向双端队列的头、尾指针 
	int r = 1;//右边界, 便于后期判断当前元素是否应该加入队列 
	for(int i = 1; i <= n; i ++) //遍历所有序号 
	{
		int left = i - k, right = i + k; //序号i最小值的下标范围[i - k, i + k]
		//将范围控制在(1, n)内,其他下标无效 
		left = max(left, 1);
		right = min(right, n); 
		
		while(hh <= tt && q[hh] < left) hh ++; //首元素对应的下标不在[left, right]内 
		
		while(r <= right) //这里可以解释为什么后期我们可以直接输出结果,因为我们限制了右边界,这样我们所寻找的最小值的下标一定在[i - k, i + k]
		{
			while(hh <= tt && a[q[tt]] > a[r]) tt --; //保证加入新元素后,单调队列仍为严格递增状态
			q[++ tt] = r; //将新元素的下标加入队列
			r ++;
		} 
		cout << a[q[hh]] << " "; //由于我们之前已经将范围确定在合理范围内,因此这里不需要在进行判断,直接输出即可 
	}

	return 0; 
}

相关题目

滑动窗口

方法三:ST表

ST表用于解决RMQ问题,初始化时间复杂度为O(nlogn),查询时间为O(1)

前导知识:ST表详解

AC代码

#include <iostream>
using namespace std;
const int N = 1000010, M = 21;
int a[N];
int Log2[N]; //log2[i]表示以2为底i的对数值 
int f[N][M]; //f[i][j]表示起始点下标为i,长度为2^j区间内的最大值 
int n;
void Log2_Init() //不使用math.h库函数中的log2,手动实现 
{
	Log2[1] = 0;
	Log2[2] = 1;
	for(int i = 3; i <= N; i ++) Log2[i] = Log2[i / 2] + 1; 
}
void ST_Init() //ST表初始化操作 
{
	for(int i = 1; i <= n; i ++) f[i][0] = a[i];
	int k = Log2[n];
	for(int j = 1; j <= k; j ++)
	{
		for(int i = 1; i <= n - (1 << j) + 1; i ++)
			f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
	}
}
int ST_Query(int l, int r) //ST表查询操作 
{
	int k = Log2[r - l + 1];
	return min(f[l][k], f[r - (1 << k) + 1][k]);
} 
int main() 
{
	Log2_Init(); 
	cin >> n;
	for(int i = 1; i <= n; i ++) cin >> a[i];
	ST_Init();
	int k; 
	cin >> k;
	for(int i = 1; i <= n; i ++)
	{
		int l = max(i - k, 1);
		int r = min(i + k, n);
		cout << ST_Query(l, r) << " ";
	}
	return 0; 
}
#include <iostream>
#include <cmath> //使用log2函数
using namespace std;
const int N = 1000010, M = 21; //由于我们还要扩大n+1~n+k的空间,因此将数组范围开大一倍 
int a[N];
int f[N][M]; //f[i][j]表示起始点下标为i,长度为2^j区间内的最大值 
int n;
void ST_Init()
{
	for(int i = 1; i <= n; i ++) f[i][0] = a[i];
	int k = log2(n);
	for(int j = 1; j <= k; j ++)
	{
		for(int i = 1; i <= n - (1 << j) + 1; i ++)
			f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
	}
}
int ST_Query(int l, int r)
{
	int k = log2(r - l + 1);
	return min(f[l][k], f[r - (1 << k) + 1][k]);
} 
int main() 
{
	cin >> n;
	for(int i = 1; i <= n; i ++) cin >> a[i];
	ST_Init();
	int k; 
	cin >> k;
	for(int i = 1; i <= n; i ++)
	{
		int l = max(i - k, 1);
		int r = min(i + k, n);
		cout << ST_Query(l, r) << " ";
	}
	return 0; 
}

相关题目

ST表模板题
欢迎大家批评指正!!!

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值