hihocoder 1074 字体设计(RMQ问题,ST算法)

题意:

抽象一下问题,现在给出输入数组 a

定义 ax 可以被 al 和 ar 插值得到为:

存在 l < x < r

使得 al ≤ ax ≤ ar 或者 al ≥ ax ≥ ar

求最少的「锚固」(固定)元素的数目,使得非锚固元素都可以由其左右最靠近它的锚固元素插值得到。并输出锚固元素的下标。

思路:

先找到整个数组的最大值和最小值,假设最小值角标 i 小于最大值角标 j ,我们必须要先固定 i 和 j ,因为这两个位置不可能通过其它的位置来固定,题目保证 ai 各不相同,所以 固定i 和 j 之后,i 和 j 之间的位置也就固定好了,,现在来考虑 j 之后的位置。对于j之后的位置,我们要考虑[j+1, n]区间的最小值,,然后固定最小值,在考虑最小值之后的位置,找最大值,固定最大值,考虑最大值之后的位置,,如此反复到数组右端。。数组左半段同理。

用ST算法求最值即可。


代码写的有点挫。。^_^

#pragma warning(disable:4996)
#include <cstdio>
#include <map>
#include <vector>
#include <algorithm>
#define N 100010
using namespace std;

int maxn[N][20], minn[N][20], a[N], n;
vector<int>ans;
map<int, int>mp;

void RMQ(){

	for (int i = 1; i <= n; i++)maxn[i][0] = minn[i][0] = a[i];

	for (int j = 1; j < 20; j++){
		for (int i = 1; i <= n; i++){
			int r = i + (1 << (j - 1));
			if (r <= n){
				maxn[i][j] = max(maxn[i][j - 1], maxn[r][j - 1]);
				minn[i][j] = min(minn[i][j - 1], minn[r][j - 1]);
			}
		}
	}
}

int query(int l, int r, int type){
	int base = 0;
	while ((1 << (base + 1)) < (r - l + 1))base++;

	if (type == 1)return mp[max(maxn[l][base], maxn[r + 1 - (1 << base)][base])];
	else return mp[min(minn[l][base], minn[r + 1 - (1 << base)][base])];
}

int main(){
	scanf("%d", &n);
	for (int i = 1; i <= n; i++){
		scanf("%d", a + i);
		mp[a[i]] = i;
	}
	RMQ();

	int i, j, x, y;

	x = query(1, n, 1);
	y = query(1, n, 2);
	if (x < y){
		ans.push_back(x);
		while (1){
			if (x>1){
				x = query(1, x - 1, 2);
				ans.push_back(x);
			}
			else break;
			if (x > 1){
				x = query(1, x - 1, 1);
				ans.push_back(x);
			}
			else break;
		}

		ans.push_back(y);
		while (1){
			if (y<n){
				y = query(y + 1, n, 1);
				ans.push_back(y);
			}
			else break;
			if (y < n){
				y = query(y + 1, n, 2);
				ans.push_back(y);
			}
			else break;
		}

	}
	else{
		int tmp = x;
		x = y;
		y = tmp;
		ans.push_back(x);
		while (1){
			if (x>1){
				x = query(1, x - 1, 1);
				ans.push_back(x);
			}
			else break;
			if (x > 1){
				x = query(1, x - 1, 2);
				ans.push_back(x);
			}
			else break;
		}

		ans.push_back(y);
		while (1){
			if (y<n){
				y = query(y + 1, n, 2);
				ans.push_back(y);
			}
			else break;
			if (y < n){
				y = query(y + 1, n, 1);
				ans.push_back(y);
			}
			else break;
		}
	}

	sort(ans.begin(), ans.end());
	printf("%d\n%d", ans.size(), ans[0]);
	for (int i = 1; i < ans.size(); i++)printf(" %d", ans[i]);
	puts("");
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值