2.12 子序列和(乘积)问题 C

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

int MaxSubsequenceSum( const int A[], int N )
{
   int ThisSum, MaxSum, j;
   ThisSum = MaxSum = 0;
   for(j=0; j<N; j++)
   {
      ThisSum+= A[j];
      if(ThisSum>MaxSum)
      MaxSum=ThisSum;
      else if(ThisSum<0)
      ThisSum=0;
   }
   return MaxSum;
}

最小子序列和 O(n) 原文

minsum的初始值是0 flag是标记数列中的某个子序列的和是否小于零(必要条件是存在负数)。否则就是不存在负数,那么返回的就是数组中的最小值。

#include<stdio.h>
 
#define MAXN 1000
int a[MAXN];
 
int n;
 
int minSubsequenceSum() {
	int ThisSum, MinSum, i,min;
 
	ThisSum = MinSum = 0;
	int flag=0;
	min = a[0];
	for (i = 0; i < n; i++) {
		ThisSum += a[i];
		if (min > a[i])
			min = a[i];
		if (ThisSum < MinSum)
		{
			MinSum = ThisSum;
			flag = 1;
		}
		else if (ThisSum>0)//任何正的子序列不可能是最小子序列的前缀
			ThisSum = 0;
	}
	if (flag)
		return MinSum;
	else
		return min;
}
 
int main() {
    int i ;
	scanf("%d", &n);
	for (i = 0; i < n; i++) {
		scanf("%d", &a[i]);
	}
	printf("%d", minSubsequenceSum());
}

最小正子序列和 O(n) 原文

将序列累计求和并存入数组,将数组排序,计算相邻两个值之差,若差大于0且在原数组中两数前后顺序相同,则记录下两数之差,遍历一遍新数组,若有符合条件的差小于前一个,则将最小差记录下来

#include<stdio.h>
//求最小正子序列和
//最小正子序列和:1、连续子序列,2、序列和为正且最小 
//思路:求出开始的一组子序列:{-2},{-2,11},
//{-2,11,-4}, {-2,11,-4,13},{-2,11,-4,13,-5},{-2,11,-4,13,-5,-2} 
//剩下的子序列必然可以通过上面的这一组子序列相减获得
//所以要获得最小正子序列各,必然将上面的序列和排序后的相邻序列和相减所得 

//其他方法:究举所有子序列,与最大子序和方法相同, 
 
//要记录秩
typedef struct node{
	int value;
	int rank;
}nodes; 
 
//快排
int quickSort(nodes a[],int low,int high){
	//int low=0,high=len-1;
	nodes pivotkey=a[low];
	while(low<high){
		while(low<high&&a[high].value>=pivotkey.value) --high;
		a[low]=a[high];
		while(low<high&&a[low].value<=pivotkey.value) ++low;
		a[high]=a[low];
	} 
	a[low]=pivotkey;
 
	return low;
 
} 
 
//快排的递归
void Qsort(nodes a[],int low,int high){
	if(low<high){
		int temp=quickSort(a,low,high);
		Qsort(a,low,temp);
		Qsort(a,temp+1,high); 
	}
} 
 
 
 
void getMin(int a[],int len){
	nodes b[len];
	int i;
	//Item *tmp = new Item[len]; 
	int sum=0;
	for( i=0;i<len;i++){
		//b[i]=(nodes)malloc(sizeof(node));
		sum+=a[i];
		b[i].value=sum;
		b[i].rank=i;
	}
	
	//将其排序 O(nlogn) 
	Qsort(b,0,len-1);
	//因为排序了最初子序列和的大小,min的计算只需要相邻子序列和相减计算
	for(i=0;i<len;i++){
		printf("%d rank为%d    ",b[i].value,b[i].rank);
	}
	printf("\n");
	
	int min=b[0].value>=0?b[0].value:b[len-1].value;
	for(i=1;i<len;i++){
		//必须得够减 
		if(b[i].rank>b[i-1].rank){
			int temp=b[i].value-b[i-1].value;
			if(temp>0&&temp<min){
				min=temp;
			}
		}
		
	} 
	printf("最小正序列和为:%d",min);
	
} 
 
 
int main(){
	int test[]={-2,11,-4,13,-5,-2};
	getMin(test,sizeof(test)/sizeof(int)); 
}

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

#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;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值