利用C语言来求最大连续子序列乘积的方法

题目描述:给一个浮点数序列,取最大乘积连续子串的值,例如 -2.5,4,0,3,0.5,8,-1,则取出的最大乘积连续子串为3,0.5,8。也就是说,上述数组中,3 0.5 8这3个数的乘积3*0.5*8=12是最大的,而且是连续的。

提醒:此最大乘积连续子串与最大乘积子序列不同,请勿混淆,前者子串要求连续,后者子序列不要求连续。也就是说:最长公共子串(Longest CommonSubstring)和最长公共子序列(LongestCommon Subsequence,LCS)的区别:

子串(Substring)是串的一个连续的部分,子序列(Subsequence)则是从不改变序列的顺序,而从序列中去掉任意的元素而获得的新序列;
    更简略地说,前者(子串)的字符的位置必须连续,后者(子序列LCS)则不必。比如字符串“ acdfg ”同“ akdfc ”的最长公共子串为“ df ”,而它们的最长公共子序列LCS是“ adf ”,LCS可以使用动态规划法解决。

解答:

解法一、穷举,所有的计算组合:
    或许,读者初看此题,自然会想到最大乘积子序列问题类似于最大子数组和问题:http://blog.csdn.net/v_JULY_v/article/details/6444021,可能立马会想到用最简单粗暴的方式:两个for循环直接轮询。

  解法二、虽说类似于最大子数组和问题,但实际上具体处理起来诸多不同。为什么呢,因为乘积子序列中有正有负也还可能有0。我们可以把问题简化成这样:数组中找一个子序列,使得它的乘积最大;同时找一个子序列,使得它的乘积最小(负数的情况)。因为虽然我们只要一个最大积,但由于负数的存在,我们同时找这两个乘积做起来反而方便。也就是说,不但记录最大乘积,也要记录最小乘积。So,我们让maxCurrent表示当前最大乘积的candidate,minCurrent反之,表示当前最小乘积的candidate,而maxProduct则记录到目前为止所有最大乘积candidates的最大值。(以上用candidate这个词是因为只是可能成为新一轮的最大/最小乘积)由于空集的乘积定义为1,在搜索数组前,maxCurrent,minCurrent,maxProduct都赋为1。
假设在任何时刻你已经有了maxCurrent和minCurrent这两个最大/最小乘积的candidates,新读入数组的元素x(i)后,新的最大乘积candidate只可能是maxCurrent或者minCurrent与x(i)的乘积中的较大者,如果x(i)<0导致maxCurrent<minCurrent,需要交换这两个candidates的值。当任何时候maxCurrent<1,由于1(空集)是比maxCurrent更好的candidate,所以更新maxCurrent为1,类似的可以更新minCurrent。任何时候maxCurrent如果比最好的maxProduct大,更新maxProduct。

解法三、本题除了上述类似最大子数组和的解法,也可以直接用动态规划求解(其实,上述的解法一本质上也是动态规划,只是解题所表现出来的具体形式与接下来的解法二不同罢了。这个不同就在于下面的解法二会写出动态规划问题中经典常见的DP方程,而解法一是直接求解)。具体解法如下:
    假设数组为a[],直接利用动归来求解,考虑到可能存在负数的情况,我们用Max来表示以a结尾的最大连续子串的乘积值,用Min表示以a结尾的最小的子串的乘积值,那么状态转移方程为:
       Max=max{a, Max[i-1]*a, Min[i-1]*a};
       Min=min{a, Max[i-1]*a, Min[i-1]*a};
 初始状态为Max[1]=Min[1]=a[1]。


下面来看一道具体的ACM题目
 题目

题目描述: 
    给定一个浮点数序列(可能有正数、0和负数),求出一个最大的连续子序列乘积。 
    输入: 
    输入可能包含多个测试样例。 
    每个测试样例的第一行仅包含正整数 n(n<=100000),表示浮点数序列的个数。 
    第二行输入n个浮点数用空格分隔。 
    输入数据保证所有数字乘积在双精度浮点数表示的范围内。 
    输出: 
    对应每个测试案例,输出序列中最大的连续子序列乘积,若乘积为浮点数请保留2位小数,如果最大乘积为负数,输出-1。 
    样例输入:

  7
  -2.5 4 0 3 0.5 8 -1
  5
  -3.2 5 -1.6 1 2.5
  5
  -1.1 2.2 -1.1 3.3 -1.1

样例输出:

  12
  64
  8.78


思路
最大连续子序列乘积和最大连续子序列和不同,这里先回忆一下最大连续子序列和的最优解结构:

最大连续子序列和
我们用sum[i]来表示以arr[i]结尾的最大连续子序列和,则状态转移方程为:

最大连续子序列乘积
考虑存在负数的情况(ps:负负会得正),因此我们用两个辅助数组,max[i]和min[i],max[i]来表示以arr[i]结尾的最大连续子序列乘积,min[i]来表示以arr[i]结尾的最小连续子序列乘积,因此状态转移方程为:

and

有了状态转移方程,dp代码就很容易实现了,看到这里还不理解的同学,我建议你多花点时间用在算法学习上吧!

AC代码

 #include <stdio.h>
  #include <stdlib.h> 

  double maxNumInThree(double a, double b, double c)
  {
    double max;
    max = (a > b) ? a : b;
    max = (max > c) ? max : c; 

    return max;
  } 

  double minNumInThree(double a, double b, double c)
  {
    double min;
    min = (a < b) ? a : b;
    min = (min < c) ? min : c; 

    return min;
  } 

  int main(void)
  {
    int i, n;
    double *arr, *max, *min, res; 

    while (scanf("%d", &n) != EOF) {
      arr = (double *)malloc(sizeof(double) * n);
      max = (double *)malloc(sizeof(double) * n);
      min = (double *)malloc(sizeof(double) * n);
      for (i = 0; i < n; i ++)
        scanf("%lf", arr + i); 

      // 动态规划求最大连续子序列乘积
      max[0] = min[0] = res = arr[0];
      for (i = 1; i < n; i ++) {
        max[i] = maxNumInThree(arr[i], max[i - 1] * arr[i], min[i - 1] * arr[i]);
        min[i] = minNumInThree(arr[i], max[i - 1] * arr[i], min[i - 1] * arr[i]);
        if (max[i] > res)
          res = max[i];
      } 

      if (res >= 0) {
        // 判断是否为浮点数
        if ((res - (int)res) == 0)
          printf("%d\n", (int)res);
        else
          printf("%.2lf\n", res);
      } else {
        printf("-1\n");
      } 

      free(arr);
    } 

    return 0;
  }

/**************************************************************
        Problem: 1501
        User: wangzhengyi
        Language: C
        Result: Accepted
        Time:110 ms
        Memory:4964 kb
    ****************************************************************/

(0)

相关推荐

  • C语言使用DP动态规划思想解最大K乘积与乘积最大问题

    最大K乘积问题 设I是一个n位十进制整数.如果将I划分为k段,则可得到k个整数.这k个整数的乘积称为I的一个k乘积.试设计一个算法,对于给定的I和k,求出I的最大k乘积. 编程任务: 对于给定的I 和k,编程计算I 的最大k 乘积. 需求输入: 输入的第1 行中有2个正整数n和k.正整数n是序列的长度:正整数k是分割的段数.接下来的一行中是一个n位十进制整数.(n<=10) 需求输出: 计算出的最大k乘积. 解题思路:DP 设w(h,k) 表示: 从第1位到第K位所组成的十进制数,设m(i,j)

  • 利用C语言来求最大连续子序列乘积的方法

    题目描述:给一个浮点数序列,取最大乘积连续子串的值,例如 -2.5,4,0,3,0.5,8,-1,则取出的最大乘积连续子串为3,0.5,8.也就是说,上述数组中,3 0.5 8这3个数的乘积3*0.5*8=12是最大的,而且是连续的. 提醒:此最大乘积连续子串与最大乘积子序列不同,请勿混淆,前者子串要求连续,后者子序列不要求连续.也就是说:最长公共子串(Longest CommonSubstring)和最长公共子序列(LongestCommon Subsequence,LCS)的区别: 子串(S

  • C语言实现求最大公约数的三种方法

    目录 题目描述 问题分析 代码实现 方法一:穷举法 方法二:辗转相除法 方法三:更相减损法 题目描述 求任意两个正整数的最大公约数 问题分析 最大公因数,也称最大公约数.最大公因子,指两个或多个整数共有约数中最大的一个.a,b的最大公约数记为(a,b),同样的,a,b,c的最大公约数记为(a,b,c),多个整数的最大公约数也有同样的记号.求最大公约数有多种方法,常见的有质因数分解法.短除法.辗转相除法.更相减损法.与最大公约数相对应的概念是最小公倍数,a,b的最小公倍数记为[a,b]. --百度

  • Python语言描述最大连续子序列和

    求最大连续子序列的和是一个很经典很古老的面试题了,记得在刚毕业找工作面试那会也遇到过同款问题.今儿突然想起来,正好快到毕业季,又该是苦逼的应届生们各种面试的时候到了,就给写了一些小代码解决这个问题.也希望各位找工作的同志们都拿到心目中理想的offer,从此以后,战胜高富帅,赢取白富美,走上人生巅峰. 1.问题描述 假设有一数组(python里为list啦)[1,3,-3,4,-6,-1],求数组中最大连续子序列的和.例如在此数组中,最大连续子序列的和为5,即1+3+(-3)+4 = 5 2.O(

  • C语言实现最长递增子序列问题的解决方法

    本文实例展示了C语言实现最长递增子序列问题的解决方法.分享给大家供大家参考.具体方法如下: 问题描述: 给定一个序列,找出其最长递增子序列长度. 比如 输入 1 3 7 5 输出 3 算法解决思路: 利用动态规划的思想,以序列的每个点最为最右端,找出每个点作为最右端时的子序列长度的最大值,即问题的求解.因此,在计算前面的每个点的时候,将其结果保存下来,后面的点与前面的点的数值进行比较,如果大,则在其长度基础上加1,并且找出所有可能情况下最长的保存为当前点的长度.形成递归. 具体实现代码如下: #

  • 解决C语言中使用scanf连续输入两个字符类型的问题

    昨天用C编程,遇到一个关于scanf的细节问题,假如运行如下程序: #include<stdio.h> int main() { char ch1,ch2; printf("Input for ch1:/n"); scanf("%c",&ch1); printf("ch1=%c/n",ch1); printf("Input for ch2:/n"); scanf("%c",&ch

  • 如何利用C语言位运算解决只出现一次的数字

    解题所需要的C语言基础知识 hello!从现在开始就进入本题解的正式内容了.首先给大家用图解的方式介绍3个C语言位运算的基本操作符 & | ^ 这些知识对下面的解题都非常重要,一定要熟练掌握,不然等会会有一种"我在哪,我是谁我在干什么"的感觉. 只出现一次的数字I 题目描述 只出现一次的数字 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? 示例 1:

  • 利用go语言实现查找二叉树中的最大宽度

    目录 介绍 流程 代码 二叉树结构体 测试代码 查找二叉树最大宽度的代码 代码解读 介绍 这道题是这样的,有一个二叉树,让求出这颗Bt树里面最大的宽度是有几个节点,同时还要求出最大宽度的这些节点在第几层? 比如:下面这颗树,它每层最大的宽度是3,所在的层数是在第3层 流程 这个题主要是使用队列的方式来存储需要遍历的节点 同时还需要几个变量来存储最大的宽度(maxWidth).每层有几个节点(count).最大宽度所在的层(maxInrow).当前层最后一个节点(currentRowEndNode

  • 利用Go语言追加内容到文件末尾

    前言 我研究了file库,终于让我找到了利用Go语言追加内容到文件末尾的办法 主要的2个函数: func (f *File) Seek(offset int64, whence int) (ret int64, err error) func (f *File) WriteAt(b []byte, off int64) (n int, err error) Seek()查到文件末尾的偏移量 WriteAt()则从偏移量开始写入 以下是例子: // fileName:文件名字(带全路径) // c

  • C#利用Random得随机数求均值、方差、正态分布的方法

    本文实例讲述了C#利用Random得随机数求均值.方差.正态分布的方法.分享给大家供大家参考.具体如下: 最近在做中小学试卷分析系统,其中数据的分析让自己很头疼,整个系统采用B/S架构.在分析试卷难度梯度的时候需要用到正态分布,自己做了一些,也查阅了一些资料,终于掌握了将一组数据分析检验,最后生成正态分布. (1)利用随机函数rand()生成(0,1)区间的100个均匀分布随机数: (2)计算这100个均匀分布随机数的均值和方差, (3)将这100个均匀分布的随机数,及其均值和方差保存到文本文件

  • SQL Server 2008 R2——查找最小nIndex,nIndex存在而nIndex+1不存在 求最小连续数组中的最大值

    其实大家稍微动下大脑,问题可以转化为,是求最小连续数组中的最大值,数组大小可以为1. ======================================================================= 做戏做全套,送佛送到西. 为了便于学习研究,必然是要写全套示例代码的. ------------------------------------------------------------------------------------- --by wls --非专

随机推荐