1. 程式人生 > 實用技巧 >2020牛客暑期多校訓練營(第五場) D-Drop Voicing

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