D. Yet Another Subarray Problem 思维 难 dp更好理解

D. Yet Another Subarray Problem

这个题目很难,我比赛没有想出来,赛后又看了很久别人的代码才理解。

这个题目他们差不多是用一个滑动窗口同时枚举左端点和右端点,具体如下:

首先枚举0~m,这个是说更新的位置,如果是1 当m==3 就更新1 4 7 10...

如果是2,当m==3 就更新 2 6 8 11....

最后都会被更新的。

核心代码

 for (int j = 0; j < n - i; ++j) {
     s += sum[j];
     if (j % m == 0) s -= k;//这个在判断是不是经过了一个区间,如果经过了就-k
     ans = max(ans, s - MIN);//这个是在判断这个时候枚举的区间左端点和区间右端点是不是可以更新答案
     if (j % m == m - 1) MIN = min(MIN, s);//这个就是在更新区间左端点,只有特定的时刻才可以更新,
    //只有在区间左端点才可以,因为我要更新的是下一个区间的右端点,如果提前更新了,那么就会被提前用,可能更新不该更新的东西。
    //左端点是由s来更新的,所以不需要考虑k的问题,
    //这个左端点只有特定时候才可以更新,因为我们枚举了每一个区间为m的起点,意思就是说我们确定了每一个区间
    //当j%m==m-1就是说到了区间的右端点,只有这个时候才可以更新,因为这个时候和我们想更新的是下一个区间的右端点,
    //所以这个才满足两点之间相隔了若干个k,这样子就可以一 前缀相减+k的个数相减*k。
 }
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 3e5 + 10;
typedef long long ll;
ll n, m, ans, k, a[maxn], sum[maxn];

int main()
{
    ans = 0;
    scanf("%lld%lld%lld", &n, &m, &k);
    for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
    for(int i=0;i<m;i++)//枚举第一个区间的起点
    {
        for (int j = i; j < n; j++) sum[j - i] = a[j];
        ll mins = 0, s = 0;
        for(int j=0;j<n-i;j++)
        {
            s += sum[j];
            if (j%m == 0) s -= k;
            ans = max(ans, s - mins);
            if (j%m == m - 1) mins = min(mins, s);
        }
    }
    printf("%lld
", ans);
}
View Code

后来上网看了题解发现这个题目还可以用dp写,感觉dp好理解很多。

dp[i][j] 表示前以 i 为右端点,对m取余为 j 时的最大值。

所以转移方程就很好写了

j== 0  dp[i][j]=max(dp[i-1][m-1]+a[i]-k,a[i]-k)

j!=0 dp[i][j]=dp[i-1][j-1]+a[i]

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 3e5 + 10;
ll dp[maxn][20];
ll a[maxn];
 
int main()
{
    int n, m, k;
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    for(int i=0;i<=n;i++)
    {
        for (int j = 0; j < m; j++) dp[i][j] = -inf64;
    }
    dp[0][m - 1] = 0;
    ll ans = 0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if (j == 0) dp[i][j] = max(dp[i - 1][m - 1] + a[i] - k, a[i] - k);
            else dp[i][j] = dp[i - 1][j - 1] + a[i];
            ans = max(ans, dp[i][j]);
        }
    }
    printf("%lld
", ans);
    return 0;
}
dp
原文地址:https://www.cnblogs.com/EchoZQN/p/11230583.html