侧边栏

2020牛客暑期多校训练营(第五场) D-Drop Voicing

链接

https://ac.nowcoder.com/acm/contest/5670/D

题意

给定1~n的排列,有两种操作

1:将倒数第二个元素放到最前面

2:将第一个元素放到最后

连续第一种操作若干次称为一段

要求将该排列变为1,2,3,...,n ,且段数尽可能少,输出这个最小值

思路

既然若干次操作1看作一段,那我们不妨将若干次操作1当作一种操作,即选择1~n-1的最右边若干元素,将它们放到最左边

或等价于选择1~n-1的某一元素,将它放在最右边

我们可以发现如果将序列放在环上操作,第二种操作就仅相当于改变数字的下标,不改变元素间的相对位置

所以我们可以将一段操作1等价于在1~n的序列中选择任一元素,将它放在最右边

那么若干段操作1我们就可以等价于将任一元素放在任意位置

于是我们只需找到环上的LIS,将其他不在LIS上的元素放到他们应该在的位置上即可

代码

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ms(a) memset(a, 0, sizeof(a))
#define repu(i, a, b) for (int i = a; i < b; i++)
#define repd(i, a, b) for (int i = a; i > b; i--)
using namespace std;
typedef long long ll;
typedef long double ld;

const int M = int(1e5) + 5;
const int mod = int(1e9) + 7;

int a[1005];
int d[1005];
int lis(int s, int t) {
    ms(d);
    repu(i, s, t + 1) {
        d[i] = 1;
        repu(j, s, i) {
            if (a[j] < a[i]) {
                d[i] = max(d[i], d[j] + 1);
            }
        }
    }
    return d[t];
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n;
    cin >> n;

    repu(i, 0, n) { cin >> a[i]; }
    repu(i, 0, n) { a[i + n] = a[i]; }

    int ans = -1;
    repu(i, 0, n + n) { ans = max(ans, lis(i, i + n - 1)); }
    cout << n - ans << endl;
    return 0;
}

posted @ 2020-07-26 17:44  晴人  阅读(115)  评论(0编辑  收藏  举报