Problem Description
Given a sequence a[1],a[2],a[3]…a[n], your job is to calculate the max sum of a sub-sequence. For example, given (6,-1,5,4,-7), the max sum in this sequence is 6 + (-1) + 5 + 4 = 14.
Input
The first line of the input contains an integer T(1<=T<=20) which means the number of test cases. Then T lines follow, each line starts with a number N(1<=N<=100000), then N integers followed(all the integers are between -1000 and 1000).
Output
For each test case, you should output two lines. The first line is “Case #:”, # means the number of the test case. The second line contains three integers, the Max Sum in the sequence, the start position of the sub-sequence, the end position of the sub-sequence. If there are more than one result, output the first one. Output a blank line between two cases.
Sample Input
2
5 6 -1 5 4 -7
7 0 6 -1 1 -6 7 -5
Sample Output
Case 1:
14 1 4
Case 2:
7 1 6
题意:求最大连续子序列的和。
思路:一开始没想着用dp做啊, 开了个循环就过了,后来回来看了下自己的代码,发现虽然不是用的dp,但还是dp 的思想呢(只不过把空间从一维缩成了常数级别)。
dp [ i ] 的意思是以数字a[i] 为结尾的最大连续子序列的和,那么状态转移方程就变成了 dp[i] = max(dp[i - 1] + a[i], a[i]);
怎么解释呢这个方程呢,如果 dp[i -1] 小于零, 即以a[i - 1]为结尾的最大连续子序列和小于零,那么以a[i] 为结尾的最大连续子序列就是a[i]。 反之则等于dp[i - 1] + a[i], 由于我们状态转移的时候只用到了上一层的状态,所以可以把空间复杂度优化为常数,时间复杂度不变。
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int n, z = 1; cin >> n;
while(n --)
{
int t, res = 0, l = 1, r, ans = -1010, len = 0; cin >> t;
for(int i = 1; i <= t; i ++)
{
len ++;
int a; scanf("%d", &a);
res += a;
if(res > ans)
{
r = i, ans = res, l = r - len + 1;
}
if(res < 0) res = 0, len = 0;
}
printf("Case %d:\n%d %d %d",z ++, ans, l, r);
if(n == 0) puts("");
else puts("\n");
}
return 0;
}
这是未优化版本。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100100;
int dp[N];
int main()
{
int n; cin >> n;
int t = 1;
while(n --)
{
int len; cin >> len;
for(int i = 1; i <= len; i ++) scanf("%d", dp + i);
int res = -1010, l = 0, r = 0, ans = 0;
for(int i = 1; i <= len; i ++)
{
dp[i] = max(dp[i - 1] + dp[i], dp[i]);
if(res < dp[i])
{
res = dp[i], r = i, l = r - ans;
}
if(dp[i] > 0) ans ++;
else ans = 0;
}
printf("Case %d:\n%d %d %d\n",t ++, res ,l, r);
//cout << res << endl;
if(n) cout << "\n";
}
return 0;
}
题意:将一串数字求m个最大连续子序列的和, 要求这些连续子序列不能重叠。
思路:一开始做的时候, wat??? 这题目什么意思,一开始还以为用前缀和来做,看了半天也没看懂题意,看了一下题解,还是不懂,后来看了一眼这道题题目是max sum plus plus ,于是去找max sum 这道题,这才明白题意,随之而来的是一脸的懵逼??这题怎么做?? 我想到用动规,但是完全不知道怎么划分状态呢,因为我前边的max sum 就没有想到是用动规(但是实际上用的思路和动规的思想一样哎,但没有从动规的角度出发),直接过了,于是我回过头去看max sum 的动规思路,emm。。 还挺简单的(见上边代码↑)。回到这个他题,我开始有模有样的做尝试,由于我先前看过一点题解,但是没看懂,第一步状态划分很顺利,f[i][j] 表示以 a[j] 为结尾的子串,分成 i 组,那么
f [i][j] = max(f [i - 1][j - k] + 以j - k + 1 为区间开头的以 j 为区间结尾且以 a[j] 作为结尾的最大连续子序列的和 ) (i < k < j), 这时候我又开始犯难,这得三层循环好吧,铁超时,换一种思路,和第一题的想法一样,就是将 a[j] 的归属分类。
则dp[i][j]=max(dp[i][j-1],max(dp[i-1][i-1]~dp[i-1][j-1]))+a[j]。
这时候我又想起了完全背包的优化方式,f[i][j - 1] = max((f [i - 1][j - k - 1] + 后边的一坨,咦,我们是不是可以把f[i][j] 分成f [i - 1][j - 1] + a[j] 和 f[i - 1][j - k] + 后边一坨加粗字号(i < k < j - 1)。 啊哈,这不就出来了吗
f[i][j] = max( f[i - 1] [j - 1] + a[j], f[i] [j - 1] + a[j])(范围的话懒得写啦)。 这样的话一定要从前边开始遍历,由于只用到
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1100000;
int f[N], a[N], t[N];
int main()
{
int n, m;
while(cin >> m >> n)
{
for(int i = 1; i <= n ; i ++) scanf("%d", a + i);
int res = -1e9;
memset(f, 0, sizeof f);
memset(t, 0, sizeof t);
for(int i = 1; i <= m ; i ++)
{
int temp = -1e9;
for(int j = i; j <= n ;j ++)
{
f[j] = max(f[j - 1] + a[j], t[j - 1] + a[j]);
t[j - 1] = temp;
temp = max(temp, f[j]);
if(i == m) res = max(res, f[j]);
}
}
cout << res << endl;//8 6 9 7 10
}
return 0;
}