2020牛客暑期多校训练营Drop Voicing(思维,LIS)

Drop Voicing

题目描述

2020牛客暑期多校训练营Drop Voicing(思维,LIS)_第1张图片

输入描述:

2020牛客暑期多校训练营Drop Voicing(思维,LIS)_第2张图片

输出描述:

在这里插入图片描述

示例1

输入

6
2 4 5 1 3 6

输出

2

说明

2020牛客暑期多校训练营Drop Voicing(思维,LIS)_第3张图片

示例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 n1个向后移动一位, 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
#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!

你可能感兴趣的:(2020牛客多校)