DP
首先可以发现要增加$m$个元素才对答案产生影响
那么记$dp[i][j]$为前$i$个且连续了$j$个元素的最大值
然后就是转移
一开始我对于每一个$i$,$j$都去枚举前$m$个元素进行转移,但这是不必要的
后来$jinzy$大佬将我的$dp$转移简化了,从原来$O(m)$的转移简化到了$O(1)$
因为确定了i和j就可以确定这串元素的开头集合,那么动态规划的性质
只要找出之前的元素进行转移即可,那么对于前面的元素可以任意选取
$jzy$大佬选了前1个元素$dp$
$jzy$大佬的程序,还有他的博客
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; const ll inf=200000000000000; int n,m; ll ans=0,dp[300005][20],sum[300005],a[300005],k; int main() { scanf("%d%d%lld",&n,&m,&k); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; } for(int i=1;i<=n;i++) { for(int j=0;j<=m;j++) dp[i][j]=-inf; } dp[1][1]=a[1]-k; for(int i=2;i<=n;i++) { dp[i][1]=a[i]-k; for(int j=1;j<=min(i,m);j++) { if(j==1) dp[i][j]=max(dp[i][j],dp[i-1][m]+a[i]-k);//其实只要从前1个元素转移过来 else dp[i][j]=max(dp[i][j],dp[i-1][j-1]+a[i]); } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) ans=max(ans,dp[i][j]); } cout<<ans<<endl; return 0; }
我当时写的暴力dp
#include <bits/stdc++.h> #define ll long long #define inf 1e18 using namespace std; const ll MAXN=3*1e5+100; ll n,m,k,dp[MAXN][12],a[MAXN],sum[MAXN]; int main() { scanf("%lld%lld%lld",&n,&m,&k); for (ll i=1;i<=n;i++) scanf("%lld",&a[i]); for (ll i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]; for (ll i=1;i<=n;i++) { for (ll j=1;j<=m;j++) dp[i][j]=-inf; } dp[1][1]=a[1]-k; for (ll i=2;i<=n;i++) { dp[i][1]=a[i]-k; for (ll j=1;j<=min(m,i);j++) { for (ll p=max((ll)1,i-m);p<i;p++) { if (j>p) continue; int d; d=i-p; if (j+d>m) dp[i][j+d-m]=max(dp[i][j+d-m],dp[p][j]+sum[i]-sum[p]-k); else dp[i][j+d]=max(dp[i][j+d],dp[p][j]+sum[i]-sum[p]); } } } ll ans=0; for (ll i=1;i<=n;i++) { for (ll j=1;j<=min(m,i);j++) ans=max(ans,dp[i][j]); } printf("%lld ",ans); }
orz jzy大佬