目录
原题:
时间限制:2 s;内存限制:512 MB。
给定一个由个整数组成的数组
。同时给定两个整数
和
。你需要执行以下操作:将
加到恰好
个不同的位置的元素上,并从其他元素中减去
。
例如,如果 ,
,
,我们选择第一个元素,那么操作后数组
。
设 表示
的子数组的最大可能和。
的子数组是
的一部分连续元素组成的
,其中
。空的子数组也应该被考虑在内,它的和为
。
设是应用上述操作后的数组
。按照最大可能的方式进行操作,使得
的值最大,并打印
的最大可能值。
输入格式
第一行包含一个整数— 测试用例的数量。
每个测试用例的第一行包含三个整数、
、
。
第二行包含 个整数
(
)。
所有测试用例中的总和不超过
。
输出格式
对于每个测试用例,输出一个整数,表示 的最大可能值。
样例输入
4
4 1 2
2 -1 2 3
2 2 3
-1 2
3 0 5
3 2 4
6 2 -8
4 -1 9 -3 7 -8
样例输出
5
7
0
44
题目大意:
给你一个数组,选k个不同的下标不同的元素,把这k个下标不同的元素的值加上x,把其他元素的值减去x,然后问操作后的数组中(选一个子数组的元素和(可以选空数组))的最大值(子数组是数组的某一段连续元素组成的数组例如 a = [1,2,3,4],[1,2,3]就是a的子数组 [1,2,4]就不是a的子数组 [1,2,5]也不是a的子数组)。
主要思路:
这个题目我们要用dp做,既然是dp,就要考虑状态的表示和转移。
状态表示:
我们一般用两维来做dp,对于这题,我们用两维表示,第一维表示前i个,第二维表示选了j个下标不同的元素。所以dp[i][j]就代表:前i个元素选了j个下标不同的元素,选一个子数组(包括空数组)的最优值。
状态转移:
我们还是分两个方面来想:
- 把当前这个数选了
- 不选当前这个数
对于第一个方面:选了就是j-1,然后i-1,并且加上x,由于可能是负数,那么就要和0取个max。就是dp[i][j] = max(dp[i][j],max(0LL,dp[i-1][j-1])+a[i]+x)
对于第一个方面:没选选了就是j,然后i-1,并且减上x,由于可能是负数,那么就要和0取个max。就是dp[i][j] = max(dp[i][j],max(0LL,dp[i-1][j])+a[i]-x)。这就是转移。
还有,只有合法才可以和答案取max,所以如果剩下的元素个数+选的个数<一共要选的个数,那么就不合法。就是if(n-i+j>=k)就合法
注意事项:
-
要记得和0取max
-
要开long long
-
注意下标访问(不要访问负下标)
代码:
#include<bits/stdc++.h>
using namespace std;
int n,k,x;
int t;
long long a[200010];
long long dp[200010][30];//开long long
int main()
{
cin>>t;
while(t--)
{
cin>>n>>k>>x;
long long ans=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=0;j<=min(i,k);j++)
{
if(i>j)//防止出现负下标
{
dp[i][j] = max(dp[i][j],max(1LL*dp[i-1][j],0LL)+a[i]-x);//和0取max
}
if(j>0)//防止出现负下标
{
dp[i][j] = max(dp[i][j],max(0LL,1LL*dp[i-1][j-1])+x+a[i]);//和0取max
}
if(n-i+j>=k)//判断是否合法
{
ans=max(ans,dp[i][j]);
}
}
}
cout<<ans<<'\n';
}
return 0;
}