Non-decreasing Array

在这里插入图片描述
题意:
每次可以两次操作
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值