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;
}

猜你喜欢

转载自www.cnblogs.com/cloudplankroader/p/13378198.html