【每日一题】数学考试

数学考试

https://ac.nowcoder.com/acm/problem/15553

Statement

今天qwb要参加一个数学考试,这套试卷一共有道题,每道题qwb能获得的分数为,qwb并不打算把这些题全做完,他想选总共道题来做,并且期望他能获得的分数尽可能的大,他准备选个不连续的长度为的区间,即
其中有

Solution

把题意简单点说:将一个数组分成两个长度均为且不相交的子串,使得这两个子串的和最大。

那么令表示第个位置前面选了个这样的区间所能得到的最大值,那么就容易得到转移式:

上面的求和式子可以用前缀和优化成

其余情况则有

于是答案就是,而不是是因为这两个区间是一定要取的而不是想取个。即全体负数的情况下也要选两个负数区间最小的。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void Main();
int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie();
  Main();
  return 0;
}

void Main() {
  int T; cin >> T;
  while(T--) {
    int n, k; cin >> n >> k;
    vector<int> a(n + 1, 0); // 获得分数
    vector<ll> pre(n + 1, 0); // 前缀和
    vector<vector<ll>> dp(n + 1, vector<ll>(3, -1e13)); // 动态规划数组
    for(int i = 1; i <= n; i++) {
      cin >> a[i];
      pre[i] = pre[i - 1] + a[i]; // 求前缀和
    }
    dp[0][0] = 0;
    for(int i = 1; i <= n; i++) {
      for(int j = 0; j < 3; j++) {
        dp[i][j] = dp[i - 1][j]; // 不以第i个数为结尾的区间能取到的最大值
      }
      if(i >= k) {
        // 与以第i个数为结尾的区间作为第一个区间能取到的最大值
        dp[i][1] = max(dp[i][1], dp[i - k][0] + pre[i] - pre[i - k]);
        // 与以第i个数为结尾的区间作为第二个区间能取到的最大值
        dp[i][2] = max(dp[i][2], dp[i - k][1] + pre[i] - pre[i - k]);
      }
    }
    cout << dp[n][2] << '\n';
  }
}
全部评论

相关推荐

4 收藏 评论
分享
牛客网
牛客企业服务