Codeforces Educational Round 123 Problem C Increase Subarray Sums (DP)
思路:
注意:此代码缺乏证明:ans[j] == max{ f[0][j] , f[1][j], …, f[n][j] }
题目让我们在数字串中,每次在不重复的数上加0 ~ n个x,并且求每次最大的子串和。
求最大子串和我们可以使用dp来求。
如:最大子序列和
这题需要在求最大子串和的时候同时计算是否在当前的数上加上x。
f[i][j]:表示以第i个数为结尾的子串的最大值,并且前i个数最多有j个数加了x
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010;
long long a[N], f[N][N], ans[N];
void sol()
{
int n, x;
cin >> n >> x;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 0; i <= n; i ++)
for(int j = 0; j <= n; j ++) f[i][j] = 0;
for(int i = 0; i <= n; i ++) ans[i] = 0;
for(int i = 1; i <= n; i ++) //先预处理不加x的情况
{
f[i][0] = max(0ll, f[i - 1][0]) + a[i];
ans[0] = max(ans[0], f[i][0]);
}
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= i; j ++)//因为前i个数最多加上j个x
{
//当前的数没有加上x
f[i][j] = max(0ll, f[i - 1][j - 1]) + a[i];
//如果当前的数加上了x
f[i][j] = max(f[i][j], max(0ll,f[i - 1][j - 1]) + a[i] + x);
/*
容易看出上面两种情况的大小只取决于x的正负
如果x大于0则加上x大,小于0则不加x大
因为x>=0
f[i][j] = max(0ll, f[i - 1][j - 1]) + a[i] + x;
*/
//更新答案
ans[j] = max(ans[j], f[i][j]);
}
/*
f[i][j]:表示以第i个数为结尾的子串的最大值,并且前i个数最多有j个数加了x
显然,对于 0 <= k <= i, f[i][k] <= f[i][j] 一定成立
所以,f[i][i]代表以第i个数为结尾的子串的最大值
由题意,前i个数最多只能加上i个x,所以当j>i时, 其最大值就是f[i][i]
形象的理解就是: 在这个字串之外也有数加了x,但是和我们这个子串没有关系
我们不用管第i个数后面的数是多少,它是否加了x,因为我们根本不去取它
*/
//更新j>i时的答案
for(int j = i + 1; j <= n; j ++)
ans[j] = max(ans[j], f[i][i]);
/*
显然,这样更新出来的答案肯定是不会漏掉任何一种的情况的。那么我们是否可以不计算j>i的情况呢?
答案是不可以。
我们在状态转移的时候更新的答案
仅仅是前i个数里最多有j个数加上了x,并且是以第i个数结尾的子串的最大值
并没有考虑到不以第i个数结尾的子串的最大值,
所以会漏掉一部分答案
*/
}
for(int i = 0; i <= n; i ++) cout << ans[i] << ' ';
cout << '\n';
}
int main()
{
int t;
cin >> t;
while(t --) sol();
return 0;
}
下面给出前缀和+枚举的做法:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010;
long long a[N], b[N], m[N], ans[N];
void sol()
{
int n, x;
cin >> n >> x;
b[0] = 0;
for(int i = 1; i <= n; i ++){
cin >> a[i];
b[i] = b[i - 1] + a[i];
}
for(int i = 0; i <= n; i ++) m[i] = -2e9;
for(int i = 1; i <= n; i ++)
for(int j = i; j <= n; j ++)
m[j - i + 1] = max(m[j - i + 1], b[j] - b[i - 1]);
//长度为1~n的子串最大和
long long curmax = 0;
for(int i = 1; i <= n; i ++) curmax = max(curmax, m[i]); //不加x的最大子串和
ans[0] = curmax;
for(int k = 1; k <= n; k ++) //加k个x
{
for(int i = k; i <= n; i ++) //长度大于等于k的都可以加k个x
curmax = max(curmax, m[i] + k * x);
ans[k] = curmax;
}
for(int i = 0; i <= n; i ++) cout << ans[i] << ' ';
cout << '\n';
}
int main()
{
int t;
cin >> t;
while(t --) sol();
return 0;
}