版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/85696474
【题目】
原题地址
给定一个序列,问最少将序列切多少刀并重排后,序列单调不降。
【解题思路】
这个
十分有趣啊。
首先我们先把所有数离散化后都用挡板分开,现在问题就是去掉最多的挡板。观察到当存在连续序列
且
在数组中不唯一时,去掉
的挡板可能导致
的挡板无法去除。
我们用 表示去除数值 到 的所有可去除挡板数量。注意到 的值和去除那一块可选挡板有关因此我们需要加一维,设 表示去除 挡板时考虑位置
即首先
和
有条件
这个条件等价于
且
不唯一。
那么对于一个
,最多产生一个冲突位置
(当a_{j’+1}唯一就不是冲突位置)。
对于一个
,只可能是一个位置
的冲突位置。
如果我们把 看作一张表,最后求的就是 的最大值。
观察转移方程,从f_i向f_{i+1}转移的时候,求 的是 这张表的所有元素,但其中有些元素加了 ( 不是 的冲突位置)。
所以,我们只需保存这张表最大的那些值就行了。具体来说我们只用保留最大值和次大值:
回顾条件,当我们保存了两个
值最大的
对应的冲突位置时,那么求
就能一定找到一个不冲突的位置,然后累计加上
所以最终复杂度为
【参考代码】
#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。
扫描二维码关注公众号,回复:
4786120 查看本文章