题目链接:
题意:给一个长为n(3e5)的数组,我们可以任意选择区间[l,r],该区间的值为sum(l~r)-k*上界((r-l+1)/m),求最大的值为多少
分析:这道题如果没有后面的需要减的,就是一个标准的最大子段和了,可以用dp的方法O(1)解决:
用dp[i]表示以i结尾的子区间最大值,有dp[i]=max(a[i],dp[i-1]+a[i])
现在有了后面要减的,情况就变复杂了很多
我写的时候,因为我觉得当前状态会收到前面的情况是第几个的影响(1~m都只需要减一个k,m+1~2m需要减2个k....),不满足dp的无后效性,就把dp做法给否了。
事实上,既然会受到前面情况是第几个的影响,那干脆第二维就说明是第几个,枚举每个情况,这样就满足无后效性了
初始化的时候要注意,一开始没初始化,调试了很久
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=1e18; const int maxn=3e5+7; const int mod=1e9+7; #define mem0(a) memset(a,0,sizeof(a)) #define meminf(a) memset(a,0x3f,sizeof(a)) #define ls rt<<1 #define rs rt<<1|1 #define mid (l+r)>>1 ll a[maxn],dp[maxn][20]; ll dp2[maxn]; int main(){ ll n,m,k;scanf("%I64d%I64d%I64d",&n,&m,&k); ll ans=0; for(int i=1;i<=n;i++){ scanf("%I64d",&a[i]); } if(m==1){ for(int i=1;i<=n;i++){ dp2[i]=max(dp2[i],dp2[i-1]+a[i]-k); ans=max(ans,dp2[i]); } printf("%I64d ",ans); return 0; } for(int i=0;i<=n;i++)for(int j=i+1;j<=m;j++)dp[i][j]=-inf; dp[0][0]=0; for(int i=1;i<=n;i++){ for(int j=0;j<m;j++){ if(i<j)break; if(j==0){ //此时可能是拿了m个的倍数或者是0个 dp[i][j]=max(dp[i-1][m-1]+a[i],(ll)0); } else if(j==1){ //重新开始了一段m,所以要减k dp[i][j]=dp[i-1][0]+a[i]-k; } else dp[i][j]=dp[i-1][j-1]+a[i]; ans=max(ans,dp[i][j]); } } printf("%I64d ",ans); return 0; }