CF1481E Sorting Books
E. Sorting Books
这个官方题解只讲了转移方程,根本没讲原理,这里全部重新做个解释。
我们先考虑答案的范围。如果我们贪心,那么最多操作次数为 \(n\) 次,且一本书最多会被移动一次。
定状态
我们令 \(l_i,r_i\) 分别为第 \(i\) 种颜色从左往右数最早出现/最晚出现的位置。令 \(f_i\) 为在最后将所有书归好类后,\([i,n]\) 内最多有多少本书无需移动。明显,我们需要倒序枚举。最后的答案为 \(n-f_1\)。
我们记 \(tot_{i,j}\) 为在 \([j,n]\) 中,颜色为 \(i\) 的书本有多少本。
列状态转移方程
\[f_i=\max\begin{cases}
f_{i+1}&1\leq i<n\\
tot_{a_i,i}&i\neq l_{a_i}\\
tot_{a_i,i}+f_{r_{a_i}+1}&i=l_{a_i}
\end{cases}\]
- 首先我们考虑的是 \(i\) 这本书要移动,那么此时最多可以保留不动的就是 \(f_{i+1}\) 本。
- 如果 \(i\) 这本书不移动,那么显然颜色为 \(a_i\) 的书都不要动,即 \(tot_{a_i,i}\) 本。那为什么在 \(i\neq l_{a_i}\),不加上 \(f_{r_{a_i}+1}\) 呢?我们可以重点关注一下样例:
5
1 2 2 1 3
若加上了,我们得到的 \(f\) 数组为 \(4,4,3,2,1\)(下标从 \(1\) 开始)。明显是错误的,应该为 \(3\)。为什么会错呢?我们来具体分析发生了甚么。
从本质出发,为什么可以保持颜色为 \(a_i\) 的书不移动,就是因为把 \([i,r_{a_i}]\) 中颜色不为 \(a_i\) 的全部移走。如果过早地进行了与有区间的合并,可能就会有两个区间有部分重叠。只有等所有颜色为 \(a_i\) 的数都出现过了,才可以进行区间的合并。
那上面那个样例说事,我们来理清关系:
- \(f_1=f_2\)
- \(f_2=f_4+1\)
- \(f_4=f_5+1\)。
好,到这里,就出现了问题。我们已经将 \([4,4],[5,5]\) 合并为了 \([4,5]\)。而在 \([2,3]\) 出现后,我们又进行了合并,有区间 \([2,5]\)。说明我们不可能将 \(2\) 移走了。我饿么您此时为了做出最优决策,只能将第一个 \(1\) 移走。明显是错误的。因为在 \([1,4]\) 区间可以被计算时,\([4,4]\) 已经“名花有主”了。
边界&初值
无。好干脆啊。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int MAXN=5e5+10;
int n;
int a[MAXN],l[MAXN],r[MAXN],f[MAXN],tot[MAXN];
signed main() {
cin>>n;
for(int i=1;i<=n;i++) {
a[i]=read();
if(!l[a[i]]) {
l[a[i]]=i;
}
}
for(int i=n;i;i--) {
if(!r[a[i]]) {
r[a[i]]=i;
}
}
for(int i=n;i;i--) {
tot[a[i]]++;
if(i==l[a[i]]) {
f[i]=max(tot[a[i]]+f[r[a[i]]+1],f[i+1]);
}
else {
f[i]=max(f[i+1],tot[a[i]]);//正确
// f[i]=max(tot[a[i]]+f[r[a[i]]+1],f[i+1];//错误
}
}
cout<<n-f[1]<<endl;
return 0;
}