最大子数组的题解

目录

原题:

时间限制:2 s;内存限制:512 MB。

输入格式

输出格式

样例输入

样例输出

题目大意:

主要思路:

状态表示:

状态转移:

注意事项:

代码:


原题:

时间限制:2 s;内存限制:512 MB。

给定一个由n个整数组成的数组a_1,a_2,...,a_n​。同时给定两个整数kx。你需要执行以下操作:将x加到恰好k个不同的位置的元素上,并从其他元素中减去x

例如,如果 a = [2,-1,2,3],k = 1x = 2,我们选择第一个元素,那么操作后数组a = [4,-3,0,1]

设 f(a) 表示a的子数组的最大可能和。a 的子数组是a 的一部分连续元素组成的a_i,a_{i+1},...,a_j,其中1 \le i \le j\le n。空的子数组也应该被考虑在内,它的和为0

a^{'}是应用上述操作后的数组a。按照最大可能的方式进行操作,使得f(a^{'})的值最大,并打印f(a^{'})的最大可能值。

输入格式

第一行包含一个整数t(1\le t \le 10^4)— 测试用例的数量。

每个测试用例的第一行包含三个整数nkx(1\le n \le 2 \times10^5,0 \le k \le min(20,n), -10^9 \le x \le 10^9)

第二行包含n 个整数a_1,a_2,...,a_n​ (-10^9 \le a_i \le 10^9)。

所有测试用例中n的总和不超过2 \times 10^5

输出格式

对于每个测试用例,输出一个整数,表示 f(a^{'})的最大可能值。

样例输入

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个下标不同的元素,选一个子数组(包括空数组)的最优值。

状态转移:

我们还是分两个方面来想:

  1. 把当前这个数选了
  2. 不选当前这个数

对于第一个方面:选了就是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)就合法

注意事项:

  1. 要记得和0取max

  2. 要开long long

  3. 注意下标访问(不要访问负下标)

代码:

#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;
}

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值