Codeforces1616 D. Keep the Average High(dp好题,子区间性质,数学证明)

文章介绍了如何使用动态规划解决一个关于区间子数组和至少为零的问题,通过分析末尾不同长度的子区间满足条件的条件,设计转移方程求解最大满足条件的子区间个数。给出的C++代码展示了具体的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

在这里插入图片描述

解法:
首先令a[i]=a[i]-x.
那么题目中子区间的条件就变为子区间的和>=0.

考虑对于一个区间,如何判断其任意子区间都满足条件.
假设a[l,i-1]满足条件,我们要添加a[i],
1.当a[i]+a[i-1]>=0满足时,我们可以将[i-1,i]组合到一起.
2.当a[i]+a[i-1]>=0以及a[i]+a[i-1]+a[i-2]>=0同时满足时,
我们可以将[l,i]组合到一起.
即只要加入a[i]之后,末尾长度为23的都满足,那么添加a[i]后的整个区间就满足.


第一个结论比较显然.
第二个结论是怎么来的呢?
考虑以i作为子区间右端点:
1.对于末尾长度=2,需要满足a[i]+a[i-1]>=0.
2.对于末尾长度=3,需要在1的基础上满足a[i]+a[i-1]+a[i-2]>=0. 
3.对于末尾长度=4,需要在12的基础上满足a[i]+a[i-1]+a[i-2]+a[i-3]>=0,
由条件1可知a[i]+a[i-1]>=0, a[i-2]+a[i-3]>=0,
因此a[i]+a[i-1]+a[i-2]+a[i-3]>=0显然成立.
4.对于末尾长度=5,同理,a[i]+a[i-1]>=0成立,a[i-2]+a[i-3]+a[i-4]>=0也成立.
因此a[i]+a[i-1]+a[i-2]+a[i-3]+a[i-4]>=0成立.

即对于右端点为i,长度>3的子区间,可以将子区间拆分为长度为23的更小区间的组合,
来证明条件成立.
所以我们只需要判断新加入的a[i],和前面区间的末尾两个数是否满足条件即可.
令d[i][j]表示前i个数,末尾j个数被选中的最大答案.
其中j的取值范围为[0,2].

转移时,我们只需要判断a[i]和a[i-1],a[i-2]的和是否满足条件,
如果满足条件则可以转移.
Code:
#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define int long long
#define PI pair<int, int>
const int maxm=2e6+5;
const int mod=998244353;

int n,x;
int a[maxm];
int d[maxm][4];
void solve(){
  cin>>n;
  for(int i=1;i<=n;i++)cin>>a[i];
  cin>>x;
  for(int i=1;i<=n;i++)a[i]-=x;
  for(int i=0;i<=n;i++){
    for(int j=0;j<=2;j++){
      d[i][j]=-1e18;
    }
  }
  d[0][0]=0;
  for(int i=1;i<=n;i++){
    int ok2=(i-1>=0)&&(a[i]+a[i-1]>=0);
    int ok3=(i-2>=0)&&(a[i]+a[i-1]+a[i-2]>=0);

    d[i][0]=max({d[i-1][0],d[i-1][1],d[i-1][2]});
    d[i][1]=max(d[i][1],d[i-1][0]+1);
    if(ok2){
      d[i][2]=max(d[i][2],d[i-1][1]+1);
    }
    if(ok2&&ok3){
      d[i][2]=max(d[i][2],d[i-1][2]+1);
    }
  }
  int ans=max({d[n][0],d[n][1],d[n][2]});
  cout<<ans<<endl;
}
signed main() {
#define MULTI_CASE
  ios::sync_with_stdio(0);
  cin.tie(0);
#ifndef ONLINE_JUDGE
  freopen("../in.txt", "r", stdin);
  freopen("../out.txt", "w", stdout);
#endif
#ifdef MULTI_CASE
  int T;
  cin >> T;
  while (T--)
#endif
    solve();
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值