双端队列

牛牛与交换顺序

题意:
牛牛有一个数组,数组元素是1到n(1<=n<=1e5)的排列,即数组的值在1~n范围内,且每个数字仅出现1次。
牛牛想要将该数组变为升序排列的,他可以进行如下的操作。
首先他要确定一个长度k,k的范围在1~n之间。
接下来他将会进行若干次操作。在每轮操作中他都可以选择一段长度为k的子数组,然后进行区间的翻转操作。
他可以做任意次数的操作,但是要求他每次选择的子数组区间满足Li<=Li+1
并且区间长度等于一开始选定的k,也就是说一旦某一次操作选择了数组的某个位置进行区间翻转操作,下一次做区间翻转的位置将会比上一次更靠右。
牛牛发现,并不总是存在一个k可以使得数组排序变为有序,请你告诉牛牛是否存在一个k能够在满足规则的情况下完成排序。

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

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

题解:找到 固定的 k 后
1.暴力reverse
2.构造双端队列(双指针思想)

暴力reverse法:
#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
const int maxn=1e5+5;
int n,a[maxn],k=1;
map<int,int> mp;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		mp[a[i]]=i;
	}
	for(int i=1;i<=n;i++){
		if(i!=a[i]){
			k=mp[i]-i+1;
			break;
		}
	}
	for(int i=1;i<=n;i++){
		if(i==a[i]) continue;
		if(i!=a[i+k-1]){
			puts("no");
			return 0;
		}
		reverse(a+i,a+i+k);
	}
	puts("yes");
	printf("%d",k);
} 
#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
const int maxn=1e5+5;
int n,a[maxn],k=1;
map<int,int> mp;

struct duilie{
	int zuo,you,flag;
	int b[2*maxn]; // k 的值确定之后,b 数组的大小也就不变了
				  //且 b 数组要开maxn两倍大小 
	void reverse(){ //flag 在 0 和 1 之间转换
		flag^=1;    //不同flag对应不同操作 ,代替翻转操作 
	}
	void push(int x){
		if(flag==0) b[you++]=x; //一开始 zuo==you 且 flag==0 ,所以you++
		else b[--zuo]=x;       //避免覆盖前面赋的值,所以--zuo 
	} 
	int front(){        //取出待判断的元素 
		if(flag==0) return b[zuo];
		else return b[you-1];	
	}
	void pop(){      //指针移动,模拟区间移动 
		if(flag==0) zuo++;
		else you--;
	}
}q;

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		mp[a[i]]=i;  //map定位,寻找 k 的值
	}
	for(int i=1;i<=n;i++){
		if(i!=a[i]){
			k=mp[i]-i+1;
			break;
		}
	}         //找到了 k
	q.zuo=maxn;  //双指针初始化
	q.you=maxn;
	for(int i=1;i<=k;i++){  // b 数组初始化
		q.push(a[i]);
	}
	for(int i=1;i<=n;i++){
		if(i!=q.front()){
			q.reverse();
			if(i!=q.front()){
				puts("no");
				return 0;
			}
		}
		q.pop();   //要在if语句的后面,因为不管是否匹配区间都要移动
		q.push(a[i+k]);
	}
	puts("yes");
	printf("%d",k);
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值