栈
jzoj 2295
题目大意:
有一个A数组,一个B数组和一个栈,现在把A数组的数存入栈(操作1),然后再从栈中取出来放在B数组,但取出可以从栈顶(操作2),也可以从栈底(操作3),现在问怎样操作可以使B数组的字典序最小
输入样例
5
1 4 3 5 2
输出样例
1 2 4 3 5
数据范围
对于10%的数据,n⩽5n \leqslant 5n⩽5;
对于30%的数据,n⩽1000n \leqslant 1000n⩽1000;
对于100%的数据,n⩽100000n \leqslant 100000n⩽100000,保证给出的AiA_iAi是1∼n1\sim n1∼n 的一个排列;
样例解释
依次使用操作 1、2、1、1、1、1、2、3、3、2 可以得到样例输出 1 2 4 3 5 。
解题思路:
因为要求的是字典序最小的,所以我们每一个数字都要尽量小,我们可以拿的有栈的两端和A数列剩下的所有数(剩下的数求最大可以用ST表),我们选最小的,如果选的是A数列剩下的数中最小的,那我们让这个数前面的数全部入栈
代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXX=100500;
int n,w,l,r,bg,s1,s2,minn,a[MAXX],p[MAXX],f[MAXX][20],s[MAXX][20];
void js(int l,int r)//查询ST表
{if (l>r){minn=MAXX;return;}int k=log(r-l+1)/log(2);if (f[l][k]<f[r-(1<<k)+1][k]){minn=f[l][k];//最小值w=s[l][k];//位置}else{minn=f[r-(1<<k)+1][k];w=s[r-(1<<k)+1][k];}
}
int main()
{scanf("%d",&n);for (int i=1;i<=n;++i){scanf("%d",&a[i]);f[i][0]=a[i];s[i][0]=i;}int t=log(n)/log(2)+1;for (int j=1;j<t;++j)//建ST表for (int i=1;i<=n-(1<<j)+1;++i)if (f[i][j-1]<f[i+(1<<(j-1))][j-1]){f[i][j]=f[i][j-1];s[i][j]=s[i][j-1];}else{f[i][j]=f[i+(1<<(j-1))][j-1];s[i][j]=s[i+(1<<(j-1))][j-1];}l=1;r=0;bg=1;a[0]=MAXX;for (int i=1;i<=n;++i){while(p[l]&&l<=r) l++;//去掉选过的while(p[r]&&l<=r) r--;if (l<=r) s1=a[l],s2=a[r];//判断栈是否为空else s1=s2=MAXX;js(bg,n);if (minn<s1&&minn<s2)//l,r表示栈的范围,bg表示A序列的开始位置{printf("%d ",minn);p[w]=1;//记录bg=w+1;//A序列的开始位置在这个点后面+1r=w-1;//前面的全部入栈}else if(s1<s2)//从栈底取出{printf("%d ",s1);p[l]=1;l++;}else{printf("%d ",s2);//从栈顶取出p[r]=1;r--;}}
}