【DP】【最大子段和】C. Functions again&D. Yet Another Subarray Problem
- 思想:
- 从头加到尾巴,如果中间加上了一个正数,那就保留下来;而如果加上了一个负数,分两种情况,一种是加上负数后,使得总的和还是大于0,那么还是得加上这个负数,因为仍然残存着余温,后面的数字借助前面的“余温”甚至可以得到更大的子段和。而如果加上后总和小于0的话,那么不如及时止损,另起炉灶。
题目经过了特殊的处理
相当于在正负正负或者负正负正这两种类型的序列中求一个最大的子段和。
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
using namespace std;
const int N = 3E5+5,M = 6E5+10;
int n,m,a[N],dp[N][2];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
repd(i,1,n) cin>>a[i];
repd(i,1,n-1)
if(i&1) dp[i][0] = abs(a[i+1]-a[i]) , dp[i][1] = -dp[i][0];
else dp[i][0] = -abs(a[i+1]-a[i]) , dp[i][1] = -dp[i][0];
ll maxn = -2e18,cura = 0, curb = 0;
repd(i,1,n-1)
{
cura += dp[i][0], curb += dp[i][1];
if(cura<0) cura = 0;
if(curb<0) curb = 0;
maxn = max({cura,curb,maxn});
}
cout<<maxn;
return 0;
}
D. Yet Another Subarray Problem
- 依题意得长度为1到m需要减去一个k,而长度为m+1到2m需要再减去一个k,以此类推。
- 由于m很小,所以可以作为dp的一个状态
- 设
dp[i][j]
表示为考虑到前i位,长度对m取余的结果为j-1的状态的最大价值。
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
using namespace std;
const int N = 3E5+5,M = 6E5+10;
ll n,m,k,a[N];
ll dp[N][15];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>k;
repd(i,1,n) cin>>a[i];
ll maxn = 0;
repd(i,1,m) dp[0][i]=-(1ll<<60);
repd(i,1,n)
repd(j,1,m)
{
if(j==1) dp[i][j] = max(dp[i-1][m],0ll) + a[i] - k;
else dp[i][j] = dp[i-1][j-1] + a[i];
maxn = max(maxn,dp[i][j]);
}
cout<<maxn;
return 0;
}