操作の順序は答えに関係ないので、最終的な筒、箱の状態に着目すると、「筒 : 配列 の連続部分列、箱:配列 から、最終時点の筒にあるの要素を除いたいたもの」となります。
以後、「 :=配列Xの要素の種類数」とします。
問題文を言い換えると、「配列 の連続部分列のうち、を満たす最長のものを求め、 からその値を引いたものを出力してください 」となります。
愚直に連続部分列の左端及び右端 を決めて種類数を求めると となり、TLEしてしまいます。
しかし、配列の長さと配列の要素の種類数は単調性あり、今回の場合でも に単調性があるので尺取り法が利用できます。
ならば右端を進め、そうでないなら左端を進めます。一連の流れの中で となるものがあれば、答えの候補としその中の最大値がを満たす最長のものです。
ある状態の とおきます。rをひとつ進めた時、 の パターンが考えられます。どのパターンにおいても が成り立つので本問題では単調性があると言えます。 についても同様です。
def f(e,A,B):
if(e not in A):
A[e] = 1
else:
A[e] += 1
B[e] -= 1
if(B[e] == 0):
del B[e]
n = int(input())
A = list(map(int,input().split()))
cntA = {}
cntB = {}
for i in range(n):
if(A[i] not in cntB):
cntB[A[i]] = 1
else:
cntB[A[i]] += 1
ans = 10**18
j = 0
for i in range(n-1):
if(len(cntA) == len(cntB)):
ans = min(ans, n-(j-i))
while j < n and len(cntA) <= len(cntB):
f(A[j], cntA, cntB)
j += 1
if(len(cntA) == len(cntB)):
ans = min(ans,n-(j-i))
f(A[i], cntB, cntA)
if(ans == 10**18):
print(-1)
exit()
print(ans)
#include<bits/stdc++.h>
using namespace std;
void push(map<int, int>& Box, int& count, int number) {
if (Box[number] == 0) count++;
Box[number]++;
}
void pop(map<int, int>& Box, int& count, int number) {
Box[number]--;
if (Box[number] == 0) count--;
}
int main() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
map<int, int>A, B;
int Ac = 0, Bc = 0;
for (int i = 0; i < n; i++) push(B, Bc, a[i]);
int ans = -1;
for (int l = 0, r = 0; l < n;) {
while (r < n && Ac <= Bc) {
push(A, Ac, a[r]);
pop(B, Bc, a[r]);
r++;
if (Ac == Bc) ans = max(ans, r - l);
}
push(B, Bc, a[l]);
pop(A, Ac, a[l]);
l++;
if (Ac == Bc) ans = max(ans, r - l);
}
if (ans == -1) {
cout << -1 << endl;
}
else {
cout << n - ans << endl;
}
}