2020牛客暑期多校训练营(第五场)D 思维|最长上升子序列
Drop Voicing
题目链接:https://ac.nowcoder.com/acm/contest/5670/D
题目描述:
给定一个n个数的序列,你可以对序列进行两种操作
操作1:将当前序列的倒数第二个元素放置在序列首部,即a1,a2,a3,,,,aN变为a1,an-1,a2,a3,,,aN
操作2:将当前序列的首部元素放置在序列尾部:即a1,a2,a3,,,aN变为a2,a3,,,aN,a1
操作2不限次数,不算操作,连续的多次进行操作1被视为一次操作,问最少需要多少次操作可以将序列转换为上升序列
思路:
可以把这个序列看成一个环,操作2可以理解成转动这个环,对当前序列元素的相对位置并无影响,由操作2我们可以得到该序列的n种排列,然后对于每种排列我们求它的LIS(最长上升子序列),当前序列的LIS代表了这个序列不需要改动位置的元素,剩下的每个元素的位置改动需要经过数次操作1后即可达到目的。所以结果就是序列长度减去所有排列种最大的LIS。
多次操作1的实质是:可以将一个数转移到任意一个位置
代码:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
int main()
{
int n;
vector<int> v;
cin>>n;
for(int i = 1; i <= n; i++){
int x;
cin>>x;
v.push_back(x);
}
int ans = 0;
for(int i = 0; i < n; i++){
vector<int> low,now;
int cnt = 0,j = i;
while(cnt<n)
{
now.push_back(v[j]);
j++;
if(j==n)j= 0;
cnt++;
}
low.push_back(now[0]);
for(j = 1; j < n; j++){
if(now[j]>low[low.size()-1]){
low.push_back(now[j]);
}else{
vector<int>::iterator ite = upper_bound(low.begin(),low.end(),now[j]);
if(ite==low.end()){
low.push_back(now[j]);
}else
*ite = now[j];
}
}
int res = low.size();
ans = max(ans,res);
}
cout<<n-ans<<endl;
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
今我来思,雨雪霏霏