题目描述
输入描述:
输出描述:
示例1
输入
6
2 4 5 1 3 6
输出
2
说明
示例2
输入
8
8 4 7 3 6 2 5 1
输出
5
题目大意
给定两个操作:
o
p
1
:
D
r
o
p
op1:\qquad Drop\,
op1:Drop把倒数第二个放到最前面。
o
p
2
:
I
n
v
e
r
t
op2:\qquad Invert\,
op2:Invert把第一个放到最后面。
现给定一个序列,问最少多少次
D
r
o
p
Drop
Drop操作后会使这个序列变成有序,期间可以任意使用
I
n
v
e
r
t
Invert
Invert。
分析
首先分析两个操作的本质,
D
r
o
p
Drop
Drop是将前
n
−
1
n-1
n−1个向后移动一位,
I
n
v
e
r
t
Invert
Invert是将整个序列向前移动一位。
那么,可以看出,
D
r
o
p
Drop
Drop操作可以将任意一个数移动到任意的位置(因为
I
n
v
e
r
t
Invert
Invert可以任意使用)。那么就是求最少要将多少个数字移动后可以变成上升序列。
于是,我们可以想到,先把原来有序的拎出来,然后一减就可以得到答案了。那么原来有序的可以用最长上升子序列来求。
可以用裸的
d
p
dp
dp,
O
(
n
3
)
O(n^3)
O(n3),反正数据小。也可以用二分优化后的,
O
(
n
2
log
n
)
O(n^2\log n)
O(n2logn)。
以下是用二分优化后的代码。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e3+10;
int a[MAXN],b[MAXN],top;
int dp(int j){
int f=0,l=top;
while(f<l){
int mid=(f+l)>>1;
if(b[mid]>a[j])l=mid;
else f=mid+1;
}return f;
}//二分的dp求最长上升子序列
int main()
{
int m,n,p,ans=0;
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%d",&a[i]),a[i+m]=a[i];//两倍,环的基本操作了
for(int i=1;i<=m;i++){
top=0; b[++top]=a[i];//用b模拟栈,可以用STL
for(int j=i+1;j<=m+i-1;j++){
if(a[j]>b[top]) b[++top]=a[j];
else b[dp(j)]=a[j];//二分找到栈中从下往上第一个大于等于当前元素的数
//并替换
}
ans=max(top,ans);//最长,无论终点
}
printf("%d\n",m-ans);//总数减一下就是要移动的数的数目
}
END
队友做的题,思路很清奇。 g r e a t ! great! great!