C语言程序设计100例之(13):最大子段和

例13        最大子段和

题目描述

给出一段序列,选出其中连续且非空的一段使得这段和最大。例如在序列2,-4,3,-1,2,-4,3中,最大的子段和为4,该子段为3,-1,2。

输入格式

第一行是一个正整数N,表示了序列的长度。

第二行包含N个绝对值不大于10000的整数Ai ,描述了这段序列。

输出格式

一个整数,为最大的子段和是多少。子段的最小长度为1。

输入样例

7

2 -4 3 -1 2 -4 3

输出样例

4

(1)编程思路。

可以从长度为n的数列的最左端(设为数组元素a[1])开始扫描,一直到最右端(设为数组元素a[n-1])为止,记下所遇到的最大总和的子序列。

程序中定义变量maxsum保存最大连续子段和,cursum保存当前连续子段和。

初始时,cursum=a[0]、maxsum=a[0]。用循环for (i=1;i<n;i++)对序列中的每一个元素a[i]进行扫描处理。

在这一扫描过程中,从左到右记录当前子序列的和(即cursum= cursum+a[i]),若这个和不断增加(即当前a[i]为正,从而使cursum+a[i]>maxsum成为可能),那么最大子序列的和maxsum也增加,从而更新maxsum。如果往右扫描中遇到负数,那么当前子序列的和cursum会减小,此时cursum将会小于maxsum,maxsum也就不更新;如果扫描到a[i]时,cursum降到0时,说明前面已经扫描的那一段就可以抛弃了,这时需要将cursum置为0。这样,cursum将从i之后的子段进行分析,若有比当前maxsum大的子段,需要更新maxsum。这样一趟扫描结束后,就可以得到正确结果。

(2)源程序。

#include <stdio.h>

int main()

{

int a[200001];

int n,i,maxsum,cursum;

scanf("%d",&n);

for (i=0;i<n;i++)

scanf("%d",&a[i]);

cursum=a[0];

maxsum=a[0];

for (i=1;i<n;i++)

{

if (cursum+a[i]>maxsum)

{

maxsum=cursum+a[i];

}

if (cursum+a[i]<0)

{

cursum=0;

}

else

cursum= cursum+a[i] ;

}

printf("%d\n",maxsum);

return 0;

}

习题13

13-1  最大差值

本题选自洛谷题库 (https://www.luogu.org/problem/P5146)

题目描述

HKE最近热衷于研究序列,有一次他发现了一个有趣的问题:

对于一个序列A1 ,A2 ?An ,找出两个数i,j,1≤i<j≤n,使得Aj −Ai 最大。

现在给出这个序列,请找出Aj −Ai 的最大值。

输入格式

第一行为一个正整数n。

接下来n个整数,第k+1个整数为Ak 。

输出格式

一行为 (Aj −Ai )的最大值

输入样例

10

1 3 4 6 7 9 10 1 2 9

输出样例

9

(1)编程思路。

由于求Aj −Ai 的最大值的要求是下标j>i,也就是说最大的数是在最小的数后面才符合要求,因此不能简单地求序列的一个最大数max和一个最小数min(无法保证max一定在min的后面),然后输出max-min作为答案。

定义变量minnum保存序列的最小数,maxdiff保存最大差值,由于输入n>=2,因此可以先输入序列前两个数a和b,赋minnum的初值为a与b中的较小数,maxdiff的初值为b-a。

然后用循环for (i=3;i<=n;i++)对序列中的每一个元素Ai进行扫描处理。处理时,若当前Ai与最小数minnum的差值大于maxdiff,则更新maxdiff,这个更新一定是有效的,因为保存的最小数minnum一定在当前数Ai之前;若当前数Ai比最小数小,则更新最小数minnum为当前数Ai。

这样一趟扫描结束后,就可以得到正确结果。

(2)源程序。

#include <stdio.h>

int main()

{

int minnum,maxdiff;

int i,n,a,b;

scanf("%d",&n);

scanf("%d%d",&a,&b);

minnum=a<b?a:b;

maxdiff=b-a;

for (i=3;i<=n;i++)

{

scanf("%d",&a);

if (a<minnum) minnum=a;

if (a-minnum>maxdiff) maxdiff=a-minnum;

}

printf("%d\n",maxdiff);

return 0;

}

13-2  连续自然数和

题目描述

对一个给定的自然数M,求出所有的连续的自然数段,这些连续的自然数段中的全部数之和为M。

例子:1998+1999+2000+2001+2002 = 10000,所以从1998到2002的一个自然数段为M=10000的一个解。

输入格式

包含一个整数的单独一行给出M的值(10≤M≤2,000,000)。

输出格式

每行两个自然数,给出一个满足条件的连续自然数段中的第一个数和最后一个数,两数之间用一个空格隔开,所有输出行的第一个按从小到大的升序排列,对于给定的输入数据,保证至少有一个解。

输入样例

10000

输出样例

18 142

297 328

388 412

1998 2002

(1)编程思路。

定义两个变量left和right用于指示待求子序列的最左端和最右端,初始时令left=1,right=(int)sqrt(2.0*n),显然此时1+2+…+right的和值sum(sum=(right-left+1)*(left+right)/2)会非常接近n。之后进行如下操作过程:

1)比较sum与n,根据sum与n的大小关系进行不同处理。简单描述就是若sum值大了,去掉子序列最左端的数,从而减少sum值;若sum值小了,将最右端数的下一个数加入子序列,从而增大sum值;若sum与n相等,则找到一个子序列的和等于n,输出此时子序列的left和right值,输出后和值sum中去掉子序列最左端的数以便继续向后找到其他的子序列。

2)重复上面的过程,直到left==right,此时子序列中只有一个数,搜索结束,退出。

(2)源程序。

#include <stdio.h>

#include <math.h>

int main()

{

int left,right,sum,n;

scanf("%d",&n);

left=1;   right=(int)sqrt(2.0*n);

sum=(right-left+1)*(left+right)/2;

while (left<right)

{

if (sum==n)

{

printf("%d %d\n",left,right);

sum-=left++;

}

else if (sum<n)

{

right++;

sum+=right;

}

else

{

sum-=left;

left++;

}

}

return 0;

}

13-3  Subsequence

本题选自北大POJ 题库(http://poj.org/problem?id=3061)。

Description

A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, and a positive integer S (S < 100 000 000) are given. Write a program to find the minimal length of the subsequence of consecutive elements of the sequence, the sum of which is greater than or equal to S.

Input

The first line is the number of test cases. For each test case the program has to read the numbers N and S, separated by an interval, from the first line. The numbers of the sequence are given in the second line of the test case, separated by intervals. The input will finish with the end of file.

Output

For each the case the program has to print the result on separate line of the output file.if no answer, print 0.

Sample Input

2

10 15

5 1 3 5 10 7 4 9 2 8

5 11

1 2 3 4 5

Sample Output

2

3

(1)编程思路。

本题题意是:输入整数N和S,N表示数列中元素的个数,S表示一个和值,然后输入数列中的N个元素,求一个子序列长度最短的连续子序列,使子序列中全部元素的和大于等于S。输出这个子序列的长度。

用两个变量i和j表示子序列的两个端点,初始时i和j的值均为0,和值sum=0。

这个搜索过程简单描述为:先让右端点移动,并将右端点的值累加到和值sum中(语句为sum+=a[j++]),直到sum大于等于S,按要求更新答案;然后再让左端点移动,并将左端点的值从和值sum中减掉(语句为sum-=a[i++];),也随之更新答案,直到出现sum小于S后,右端点再移动。重复这个过程,直到右端点越过了原序列的最后一个数据。

(2)源程序。

#include <stdio.h>

int main()

{

int t,i,j,a[100001],n,s,sum,minx;

scanf("%d",&t);

while(t--)

{

scanf("%d%d",&n,&s);

for(i=0;i<n;i++)

scanf("%d",&a[i]);

i=0;

minx=0x7fffffff;

sum=0;

for (j=0;j<n;)

{

while(sum<s && j<n)

sum+=a[j++];

while(sum>=s)

{

if (minx>j-i) minx=j-i;

sum-=a[i++];

}

}

if (minx==0x7fffffff)

minx=0;

printf("%d\n",minx);

}

return 0;

}

原文地址:https://www.cnblogs.com/cs-whut/p/11902351.html

时间: 2024-10-29 03:53:03

C语言程序设计100例之(13):最大子段和的相关文章

经典C语言程序设计100例 -- C 和 Python 版 (06 - 10)

[06]格式化输出 题目:用*号输出字母C的图案. 思路:可先用'*'号在纸上写出字母C,再分行输出.如果输出图形较大,且有规律可循,可考虑使用循环. C 语言代码 int main() { const char *p = " **** \n" " ** ** \n" "** \n" "** \n" "** \n" " ** ** \n" " **** \n"; pr

黑马程序员——经典C语言程序设计100例

1.数字排列 2.奖金分配问题 3.已知条件求解整数 4.输入日期判断第几天 5.输入整数进行排序 6.用*号显示字母C的图案 7.显示特殊图案 8.打印九九口诀 9.输出国际象棋棋盘 10.打印楼梯并按条件打印笑脸 11.经典兔子问题 12.判断素数 13.水仙花数问题 14.正整数分解质因数 15.学习成绩划分 16.正整数求其最大公约数和最小公倍数 17.统计英文字母/空格/数字个数 18.求s=a+aa+aaa+aa...a的值 19.求解"完数" 20.球体自由落下物理问题

C语言程序设计100例之(4):水仙花数

例4    水仙花数 题目描述 一个三位整数(100-999),若各位数的立方和等于该数自身,则称其为“水仙花数”(如:153=13+53+33),找出所有的这种数. 输入格式 没有输入 输出格式 若干行,每行1个数字. 输入样例 无 输出样例 153 * * * ... * * * (输出被和谐了) (1)编程思路1. 对三位数n(n为100~999之间的整数)进行穷举.对每个枚举的n,分解出其百位a(a=n/100).十位b(b=n/10%10)和个位c( c=n%10),若满足a*a*a+

C语言程序设计100例之(3): Cantor表

例3    Cantor表 题目描述 现代数学的著名证明之一是Georg Cantor证明了有理数是可枚举的.他是用下面这一张表来证明这一命题的: 1/1  1/2  1/3  1/4  …… 2/1  2/2  2/3  …… 3/1  3/2  …… 4/1  …… …… 现以z字型方法给上表的每项编号.方法为:第一项是1/1,然后是1/2.2/1.3/1.2/2.1/3.1/4.2/3……. 输入格式 整数N(1≤N≤10000000) 输出格式 表中的第N项 输入样例 7 输出样例 1/

C语言程序设计100例之(9):生理周期

例9    生理周期 问题描述 人生来就有三个生理周期,分别为体力.感情和智力周期,它们的周期长度为 23 天.28 天和33 天.每一个周期中有一天是高峰.在高峰这天,人会在相应的方面表现出色.例如,智力周期的高峰,人会思维敏捷,精力容易高度集中.因为三个周期的周长不同,所以通常三个周期的高峰不会落在同一天.对于每个人,我们想知道何时三个高峰落在同一天. 对于每个周期,我们会给出从当前年份的第一天开始,到出现高峰的天数(不一定是第一次高峰出现的时间).你的任务是给定一个从当年第一天开始数的天数

C语言程序设计100例之(14):丑数

例14   丑数 问题描述 丑数是其质因子只可能是2,3或5的数.前10个丑数分别为1, 2, 3, 4, 5, 6, 8, 9, 10, 12.输入一个正整数n,求第n个丑数. 输入格式 每行为一个正整数n (n <= 1500),输入n=0结束. 输出格式 每行输出一个整数,表示求得的第n个丑数. 输入样例 1 2 50 0 输出样例 1 2 243 (1)编程思路. 根据丑数的定义,丑数从小到大排列的序列中的一个数应该是其前面某个数乘以2.3或者5的结果.因此,可以定义一个数组num[15

C语言程序设计100例之(22):插入排序

例22  插入排序 问题描述 排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素或记录的任意序列,重新排列成一个以关键字递增(或递减)排列的有序序列. 排序的方法有很多,简单插入排序就是一种简单的排序算法. 插入排序的基本思想是顺序将一个待排序的记录按其关键字值的大小插入到一个有序的序列中,插入后该序列仍然是有序的. 简单插入排序是一种最简单的排序方法.它的排序过程为:先将待排序序列中第1个记录看成是一个有序的子序列,然后从第2个记录起依次逐个地插入到这个有序的子序列中去.这很像玩扑

C语言程序设计100例之(21):折半查找

例21  折半查找 问题描述 顺序查找是一种最简单和最基本的检索方法.其基本思想是:从检索表的一端(如表中第一个记录或最后一个记录)开始,逐个进行记录的关键字和给定值的比较.若某个记录的关键字和给定值比较相等,则查找成功:否则,若直至检索表的另一端(如最后一个记录或第一个记录),其关键字和给定值比较都不等,则表明表中没有待查记录,查找不成功. 顺序查找可以写成一个简单的一重循环,循环中依次将检索表(不妨设为数组a)中的元素与给定值比较,若相等,用break退出循环.算法描述为: for (i=0

C语言程序设计100例之(20):过河卒

例20  过河卒 题目描述 如图1,在棋盘的A点有一个过河卒,需要走到目标B点.卒行走规则:可以向下.或者向右.同时在棋盘上的任一点有一个对方的马(如图1的C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点.例如,图1中C点上的马可以控制9个点(图中的P1,P2,…,P8 和C).卒不能通过对方马的控制点. 棋盘用坐标表示,A点(0,0).B点(n,m)(n,m为不超过50的整数,并由键盘输入),同样马的位置坐标通过键盘输入,并约定C<>A,同时C<>B. 编写程序,计算