题目大意:把整数序列A变成一个单调严格上升的序列。要求改变的数尽量少,在满足此前提下改变幅度最小
题解:
第一问:补集转换,题目转换为:最大化不修改的点。
对于任意的
i,j(j<i)
,若可以通过修改中间的
j−i+1
个数来使得
[j,i]
满足要求,必要条件是
a[i]−a[j]>=i−j
,不妨设b[i]=a[i]-i,则条件变为b[i]>=b[j],求出b[i]的最长不降子序列长度len,
ans=n−len
第二问:
结论:如果从j转移到i的话,那么中间一定有一个k(k>=j&&k < <script type="math/tex" id="MathJax-Element-205"><</script>i),使得
的高度都是a[j],k+1~i的高度都是i,且这样的花费是最优的
在枚举f[i]中,第i个数是保证不改的,所以第n个数不管怎样都不会被改。所以我们要在第一问前就在序列尾加一个无穷大的元素。
在序列的前后各插入一个数,这样好写一些
我的收获:强啊
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int M=35005;
#define LNF 1e60
#define INF 0x3f3f3f3f
int n,ret,t;
int a[M],f[M],k[M],head[M];
long long g[M],sum1[M],sum2[M];
struct edge{int to,nex;}e[M*10];
void add(int u,int v){e[t].to=v,e[t].nex=head[u],head[u]=t++;}
void lis()
{
memset(k,0x3f,sizeof(k));
for(int i=1,pos;i<=n;i++)
pos=upper_bound(k+1,k+n+1,a[i])-k,k[f[i]=pos]=a[i],ret=max(ret,f[i]);
cout<<n-ret<<endl;
}
void solve()
{
t=0;memset(head,-1,sizeof(head));
for(int i=n;i>=0;i--) add(f[i],i),g[i]=LNF;
g[0]=0;a[0]=-INF;
for(int i=1;i<=n;i++)
for(int j=head[f[i]-1];j!=-1;j=e[j].nex)
{
int v=e[j].to;
if(v>i) break;
if(a[v]>a[i]) continue;
for(int k=v;k<=i;k++) sum1[k]=abs(a[k]-a[v]),sum2[k]=abs(a[k]-a[i]);
for(int k=v+1;k<=i;k++) sum1[k]+=sum1[k-1],sum2[k]+=sum2[k-1];
for(int k=v;k<i;k++) g[i]=min(g[i],g[v]+sum1[k]-sum1[v]+sum2[i]-sum2[k]);
}
cout<<g[n]<<endl;
}
void work()
{
lis();
solve();
}
void init()
{
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),a[i]-=i;
a[++n]=INF-1;//要比INF小
}
int main()
{
init();
work();
}