题意:
每次可以两次操作
1:改变一个数
2:删除一个数
注:改变数之后仍然是一个递增的序列,注意a[1]和a[n]不能删除
改变一个数相当于删除一个数,也就是每次我们就是进行一次操作相当于删除两个数
假设 1 2 3:我们将2改变成1就相当于删除这个2
这里可能会问了,我们改变的实际过程中我改变后面的堆积那么多数会不会对之后的操作产生影响?
这是不会的。
因为我们会发现,我们最多进行(n-2)/2上取整次操作就可以将答案变为
(
a
[
n
]
−
a
[
1
]
)
2
(a[n]-a[1])^{2}
(a[n]−a[1])2
我们假设是1 3 4 7 9 10:
删3 将4变为1
删7 将9变为1
最终是1 1 1 10
所以我们最多左(n-1)上取整次操作
所以我们只能在1~(n-1)/2内讨论即可,看是否可以将改变一个数看作删除一个数:
我们可以反着考虑,看是否通过操作能否达到那种删掉数之后的状态
我们可以试试:
我们删除两个数(进行一次操作)看能否1 4 9 10的状态
3变为1,删除7
可以多试几组,对于每一组一定是有相应的操作可以完成的,所以dp一定要有全局意识,等到这个状态完成后再去看怎么形成,而不是去一步一步推状态怎么实现的。
知道这个了,dp就很简单了,dp[i][j]表示在(1,i)中删除了j个数的最大值。
下面是AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define int long long
int max(int x,int y)
{
if(x>y) return x;
else return y;
}
const int N=110;
int a[N];
int prefix[N];
int dp[N][N];//dp[i][j][k]表示到i,删除j个,a[1]和a[i]一定不会被删除
int get(int l,int r)
{
return (a[r]-a[l])*(a[r]-a[l]);
}
signed main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=2;i<=n-1;i++)
{
prefix[i]=prefix[i-1]+(a[i]-a[i-1])*(a[i]-a[i-1]);
}
for(int i=1;i<=n;i++)
{
dp[i][0]=prefix[i];
}
for(int i=1;i<=n;i++)//枚举到的位置
{
for(int j=1;j<=n-2;j++)//枚举删除的数量
{
for(int k=max(1,i-j-1);k<=i-1;k++)//枚举上一个未被删的位置
{
dp[i][j]=max(dp[i][j],dp[k][j-(i-k-1)]+get(i,k));
}
}
}
for(int i=1;i<=n;i++)
{
int x=min(2*i,n-2);
cout<<dp[n][x]<<endl;
}
return 0;
}