双端队列——牛牛与交换排序

牛牛与交换排序

    • 题目描述
    • 输入描述
    • 输出描述
    • 解题思路
    • 解题技巧
    • c++代码

题目描述

链接:https://ac.nowcoder.com/acm/contest/9982/F
来源:牛客网

牛牛有一个数组,数组元素是1到n的排列,即数组的值在1~n范围内,且每个数字仅出现1次。
牛牛想要将该数组变为升序排列的,他可以进行如下的操作。

首先他要确定一个长度k,k的范围在1~n之间。
接下来他将会进行若干次操作。在每轮操作中他都可以选择一段长度为k的子数组,然后进行区间的翻转操作。

他可以做任意次数的操作,但是要求他每次选择的子数组区间满足Li <= Li+1,并且区间长度等于一开始选定的k,也就是说一旦某一次操作选择了数组的某个位置进行区间翻转操作,下一次做区间翻转的位置将会比上一次更靠右。

牛牛发现,并不总是存在一个k可以使得数组排序变为有序,请你告诉牛牛是否存在一个k能够在满足规则的情况下完成排序。

输入描述

第一行输入一个正整数n(1 <= n <= 1e5)表示数组的大小。
接下来输出一行n个正整数表示一个排列,即每个数的大小范围在1到n且每个正整数仅出现一次。

输出描述

如果存在至少一个k能够使牛牛完成排序,请先在一行中输出一个"yes",然后另起一行输出一个可以满足排序的k,要求k的范围在[1,n]之间,如果有多解,你可以输出任意一个。反之如果不存在任何一个k可以完成排序,请直接在一行输出一个"no"

解题思路

抓住题目的重点下一个做区间翻转的位置将会比上一次更靠右,且翻转区间长度为k是不变的,顺着这个思路,当数组 f 的下标为 i 的元素的值不等于 i ,我们需要在数组中找到一个元素的值等于 i ,这两个元素的之间的距离就是翻转区间k。所以我们只需要在确定 k 之后遍历一遍数组,当下标与元素的值不相等时翻转,若翻转后还是不相等,则直接判定为不能完成排序(因为遍历到当前元素后面的元素时已经无法改变前面元素的顺序)。

解题技巧

频繁进行数组翻转操作效率较低,可以使用双端队列,通过不同端的入队及出队来模拟翻转。

c++代码

#include
#include
using namespace std;
const int maxn = 1e5 + 5;

int f[maxn], n, k;
int ff[maxn];

int main() {
	cin >> n;
	for(int i = 1; i <= n; i++) {
		scanf("%d", &f[i]);
		if(f[i] == 1 && i != 1) k = i;
	}
	int flag = 0, pos;
	//寻找表示区间长 k ,以及第一个下标与值不相等的元素 pos 
	if(!k) {
		for(int i = 1; i <=n; i++) {
			if(!flag && i != f[i]) {
				pos = i; flag = 1;	
			}
			else if(flag &&f[i] == pos){
				k = i - pos + 1; break;
			}
		}
	}
	if(!k) { printf("yes\n1"); return 0; }

	deque q;//双端队列 
	for(int i = pos; i < pos+k-1; i++) {
		q.push_back(f[i]);
	}
	flag = 0;
	int t = 0;
	int temp = pos;
	for(int i = pos+k-1; i <= n; i++) {
		if(!t) q.push_back(f[i]); //t=0表示从尾巴入队以及出队 
		else q.push_front(f[i]);//t=1表示从头入队以及出队 
		if(q.back() == temp) {
			q.pop_back(); t = 1;
		}
		else if(q.front() == temp) {
			q.pop_front(); t = 0;
		}
		else {
			flag = 1; break;
		}
		temp++;
	}
	if(!flag) {//最后还需判断队列里面的元素是否排序正确 
		int j = 0;
		for(deque::iterator it = q.begin(); it != q.end(); it++) {
			ff[j++] = *it;
		}
		if(ff[0] == temp) {
			for(int i = 0; i < j; i++) {
				if(ff[i] != temp+i) flag = 1;
			}
		}
		else {
			for(int i = 0; i < j; i++) {
				if(ff[i] != n-i) flag = 1;
			}
		}
	}
	if(flag) printf("no");
	else printf("yes\n%d", k);
	return 0;
}

你可能感兴趣的:(数据结构与算法,算法,队列,c++,数据结构)