【最大子段和DP】【前缀和】C. Increase Subarray Sums

108 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目链接:
codeforces.com/contest/164…

f(k)f(k)表示对一个数组中不同的k个元素做加x操作,最后的最大字段和。 求f(0),f(1),...,f(n)f(0),f(1),...,f(n)


对数组a做前缀和

状态表示:

f[ij+1]f[i-j+1]:子段长度为i - j + 1的最大子段和

状态转移:

f[ij+1]=max(f[ij+1],s[i]s[j1])f[i-j+1] = max(f[i-j+1],s[i]-s[j -1])

这个用动态规划求解最大子段和,复杂度O(N2)O(N^2)

f[0] = 0;
for(int i = 1; i <= n; i++)
{
	for(int j = 1; j <= i; j++)
		f[i - j + 1] = max(f[i - j + 1], s[i] - s[j - 1]);
}

因为还有增强的个数,所以最后统计需要加上增强的个数。

假设增强了j次,统计最大子段的长度为i,那么最多增强min(i,j)min(i,j)次,在所有的情况中取最佳即可


#include<bits/stdc++.h>
using namespace std;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t;
	cin >> t;
	while(t--)
	{
		int n, x;
		cin >> n >> x;
		
		vector<int> a(n + 1), s(n + 1, 0);
		for(int i = 1; i <= n; i++)
		{
			cin >> a[i];
			s[i] = s[i - 1] + a[i];
		}
		
		vector<int> f(n + 1, -1e9);
		f[0] = 0;
		
		for(int i = 1; i <= n; i++)
		{
			for(int j = 1; j <= i; j++)
				f[i - j + 1] = max(f[i - j + 1], s[i] - s[j - 1]);
		}
		
		for(int i = 0; i <= n; i++)
		{
			int res = 0;
			for(int j = 0; j <= n; j++)
				res = max(res, f[j] + min(i, j) * x);
			cout << res << " \n"[i == n];
		}
	}
	return 0;
}