洛谷P1115 最大子段和 (线性DP)
经典的线性DP例题,用f[i]表示以第i个位置结尾的最大连续子段和。
状态转移方程:f[i]=max(f[i],f[i-1]+a[i]);
这里省去了a数组,直接用f数组读数据,如果f[i-1]<0,那么f[i]肯定不会加上它,f[i]=a[i],相当于是从此时的i位置重新计算最大连续子段和;如果f[i-1]>=0,那它对f[i]来说是有贡献的,要加上它。
代码很短:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 2e5 + 10, inf = 1e9; 4 int n, f[N], ans = -inf; 5 int main() { 6 cin >> n; 7 for (int i = 1; i <= n; i ++) { 8 cin >> f[i]; 9 f[i] = max(f[i], f[i - 1] + f[i]); 10 ans = max(ans, f[i]); 11 } 12 cout << ans; 13 }
还可以对空间进行优化,我们可以发现,f[i]的值只可能与它前一个(即f[i-1])有关,用滚动数组就行了,这里就不再写上代码了。
update221010
另一种方法,一个连续子段可以转化为两个前缀相减,我们可以枚举后面的前缀,不断更新前面的前缀的最小值,更新答案即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 2e5 + 10, inf = 1e9 + 10; 4 int n, sum[N]; 5 6 int main() { 7 scanf("%d", &n); 8 int ans = -inf, l = 0; 9 for (int i = 1; i <= n; i ++) { 10 scanf("%d", &sum[i]); 11 sum[i] += sum[i - 1]; 12 if (sum[i] - sum[l] > ans) ans = sum[i] - sum[l]; 13 if (sum[i] < sum[l]) l = i; 14 } 15 printf("%d\n", ans); 16 return 0; 17 }