sincerit 算法竞赛宝典 最大连续子序列积及最大连续子序列和(经典问题)

最大连续子序列和
给定一个数列,其中可能有正数也可能有负数,我们的任务是找出其中连续的一个子数列(不允许空序列),使它们的和尽可能大
输入
5
1 2 -5 11 3
输出
14
分析:设sum为连续子序列和,先从1开始,加2,再加-5,当加到-5时,sum=-2 < 0, 此时如果再加上11的话,sum=9, 我们发现-2明显拖了后腿,如果没有-2(前面序列舍去),只取第四个数据的话, sum=11会是更好的选择。
由此可以得到一个结论,当sum < 0时,可以直接舍去前面的值,把sum重新赋值为0,这样效率就是o(n)

#include <stdio.h>
#include <cstring>
int main() {
  int n;
  int sum = 0, t = 0, x;
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &x);
    t += x;
    if (t < 0) t = 0;
    if (sum < t) sum = t;
  }
  printf("%d\n", sum);
  return 0;
}
还有如果全部是负数的情况要怎么办呢?上面的代码是求不出答案的
最后改进代码就是

#include <stdio.h>
#include <cstring>
int main() {
  long long n, x, sum;
  scanf("%lld", &n);
  scanf("%lld", &x);
  sum = x;
  long long t = x; // 用来累加
  for (int i = 2; i <= n; i++) {
    scanf("%lld", &x);
    t += x;
    if (sum < t) sum = t;
    if (t < 0) {
      if (x > 0) t = x; 舍去前面的之后,下一个开始的数肯定要是整数,不可能选一个负数开头
      else t = 0;
    }
  }
  printf("%lld\n", sum);
  return 0;
}
// -2 1 -3 4 -1 2 1 -5 4  这个例子的答案是6, 4,-1,2,1

如果要输出序列的首尾位置呢?
这有个例题:https://blog.csdn.net/sincerit/article/details/83041762

最大连续子序列积
找出一个序列中乘积最大的连续子序列(至少包含一个数)并输出最大值。
输入
输入一个n,代表数的个数,以下n个数字中间以空格隔开
5
-5 3 9 10 -5
输出
6750

解决了最大连续子序列和的问题,最大连续子序列积问题要怎么办呢,肯定不是像最大连续子序列和一样去解决
这里要注意,由于存在负数,我们不能简单保存一个当前的最大值,还应保存一个最小值,因为有可能一个最小值再乘一个负数,得到一个最大值
思路:令f[n]表示[0,n]区间内,以n结尾的最大积,令g[n]表示[0,n]区间内,以n结尾的最小积,num为原序列则:
f[n] = max{f[n-1]*num[n], num[n], g[n-1]*num[n]}
g[n] = min{f[n-1]*num[n], num[n], g[n-1]*num[n]}
如下代码

#include <stdio.h>
#include <cstring>
long long max(long long a, long long b) {
  return a>b?a:b;
}
long long min(long long a, long long b) {
  return a>b?b:a;
}
long long num[100];
long long f[100]; 
int main() {
  int n;
  scanf("%d", &n);
  for (int i = 0; i < n; i++) scanf("%lld", &num[i]);
  long long sum = num[0]; // 只有一个数的时候 
  long long tempmx = num[0], tempmi = num[0]; // 存放到当前位置是的最大最小的乘积 f[n], g[n]
  for (int i = 1; i < n; i++) {
    int fmx = tempmx;  // f[n-1]
    int fmi = tempmi;  // g[n-1]
    tempmx = max(num[i], max(num[i]*fmx, num[i]*fmi)); // 当前最大乘积 = max(当前数, max(当前数*之前的乘积最大,当前数*之前的乘积最小)) f[n] =  max{f[n-1]*num[n],  num[n],  g[n-1]*num[n]}
    tempmi = min(num[i], min(num[i]*fmx, num[i]*fmi)); // 再维护到当前的最小值 
    sum = max(tempmx, sum);
  }
  printf("%lld", sum);
  return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值