LIS(思维题) - Drop Voicing - 2020牛客暑期多校训练营(第五场)
题意:
给定一个长度为n的排列(n个整数都介于[1,n],且不重复),允许两种操作:
①、Drop−2:将倒数第二个数放到开头。
②、Invert:将开头的第一个数放到最后。
每次操作允许选择①或②连续进行多次,
求最少需要进行几次①操作(每次可以连续多次将倒数第二个数放到开头),
可将排列排好序(从1到n递增)。
示例1
输入:
6
2 4 5 1 3 6
输出
2
说明
An optimal solution with two multi-drops is:
- Invert, 5 times, changing the permutation to 6,2,4,5,1,3;
- Drop-2, 3 times, changing the permutation to 4,5,1,6,2,3;
- Invert, 4 times, changing the permutation to 2,3,4,5,1,6;
- Drop-2, 1 time, changing the permutation to 1,2,3,4,5,6.
示例2
输入
8
8 4 7 3 6 2 5 1
输出
5
分析:
观察操作①和操作②,我们不妨将整个排列看作是一个环。
我们分析操作①,即每次可以将连续的某一段数pk,pk+1,...,pn−1逆序后移动到开头,
在环上相当于将这一段逆时针旋转移动到开头,
也就是说,pk,pk+1,...,pn−1相对于pn逆时针移动到了p1,p2,...,pk−1这一段的前面,
等价于将pn移动到了环上的某个位置上。
因此操作①可归纳为:将序列中的任意一个数移动到任意位置上。
分析操作②,我们发现环上任意两数之间的相对位置不改变,因此,操作②对排序无作用。
所以,要求连续进行操作①的最少次数,我们需要找到序列上的最长上升子序列,
在最长上升子序列中插入其他的数,最终答案即序列长度n−环上最长上升子序列的长度。
具体做法:
从1到n枚举环的起点i,对每个起点i求一遍最长上升子序列的DP,保存最大值maxl,输出n−maxl。
时间复杂度:
O(n3)
代码:
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=510;
int n,a[N<<1];
int f[N<<1];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[n+i]=a[i];
}
int maxl=0;
for(int i=1;i<=n;i++)
for(int j=i;j<=i+n-1;j++)
{
f[j]=1;
for(int k=i;k<j;k++)
if(a[k]<a[j])
f[j]=max(f[j],f[k]+1);
maxl=max(maxl,f[j]);
}
cout<<n-maxl<<endl;
return 0;
}