【题目】
原题地址
给定一个序列,问最少将序列切多少刀并重排后,序列单调不降。
n
≤
1
0
6
n\leq 10^6
n≤106
【解题思路】
这个
DP
\text{DP}
DP十分有趣啊。
首先我们先把所有数离散化后都用挡板分开,现在问题就是去掉最多的挡板。观察到当存在连续序列
(
a
,
a
+
1
,
a
+
2
)
(a,a+1,a+2)
(a,a+1,a+2)且
a
+
1
a+1
a+1在数组中不唯一时,去掉
(
a
,
a
+
1
)
(a,a+1)
(a,a+1)的挡板可能导致
(
a
+
1
,
a
+
2
)
(a+1,a+2)
(a+1,a+2)的挡板无法去除。
我们用 f i f_i fi表示去除数值 ( 0 , 1 ) (0,1) (0,1)到 ( i , i + 1 ) (i,i+1) (i,i+1)的所有可去除挡板数量。注意到 f f f的值和去除那一块可选挡板有关因此我们需要加一维,设 f i , j f_{i,j} fi,j表示去除 ( i , i + 1 ) (i,i+1) (i,i+1)挡板时考虑位置 ( j , j + 1 ) (j,j+1) (j,j+1)
即首先
i
i
i和
j
j
j有条件
a
j
=
i
,
a
j
+
1
=
i
+
1
a_j=i,a_{j+1}=i+1
aj=i,aj+1=i+1
f
i
,
j
=
max
(
f
i
−
1
,
j
′
+
[
j
不
是
j
′
产
生
的
冲
突
位
置
]
)
f_{i,j}=\max(f_{i-1,j'}+[j不是j'产生的冲突位置])
fi,j=max(fi−1,j′+[j不是j′产生的冲突位置])
这个条件等价于
j
=
j
′
+
1
j=j'+1
j=j′+1且
a
j
a_j
aj不唯一。
那么对于一个
j
′
j'
j′,最多产生一个冲突位置
j
′
+
1
j'+1
j′+1(当a_{j’+1}唯一就不是冲突位置)。
对于一个
j
j
j,只可能是一个位置
j
′
=
j
−
1
j'=j-1
j′=j−1的冲突位置。
如果我们把 f i f_i fi看作一张表,最后求的就是 f n f_n fn的最大值。
观察转移方程,从f_i向f_{i+1}转移的时候,求 max \max max的是 f i f_i fi这张表的所有元素,但其中有些元素加了 1 1 1( j j j不是 j ′ j' j′的冲突位置)。
所以,我们只需保存这张表最大的那些值就行了。具体来说我们只用保留最大值和次大值:
回顾条件,当我们保存了两个
f
f
f值最大的
j
′
j'
j′对应的冲突位置时,那么求
f
i
,
j
f_{i,j}
fi,j就能一定找到一个不冲突的位置,然后累计加上
1
1
1
所以最终复杂度为 O ( n ) O(n) O(n)
【参考代码】
#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e6+10;
int n,sz,b[N];
vector<int>a,pos[N];
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return f?ret:-ret;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
#endif
n=read();
for(int i=1;i<=n;++i)
{
int x=read();
if(a.empty() || a.back()!=x) a.pb(x);
}
n=(int)a.size();
for(int i=0;i<n;++i) b[i]=a[i];
sort(b,b+n);sz=unique(b,b+n)-b;
for(int i=0;i<n;++i) a[i]=lower_bound(b,b+sz,a[i])-b,pos[a[i]].pb(i);
pii s1=mkp(0,n),s2=mkp(0,n);
for(int i=0;i<sz-1;++i)
{
pii t1=s1,t2=s2;
for(int j=0;j<(int)pos[i].size();++j)
{
int p=pos[i][j];
if(p==n-1 || a[p]+1!=a[p+1]) continue;
pii s=mkp(0,n);
if(p^s1.se) s=s1; else s=s2;
++s.fi;s.se=p+1;
if(pos[i+1].size()==1) s.se=n;
if(s>t1) t2=t1,t1=s;
else if(s>t2) t2=s;
}
s1=t1;s2=t2;
}
printf("%d",n-s1.fi-1);
return 0;
}
【总结】
层层递进的思想,挺难想的吧qwq。