迷之逆序 最大连续子序列和

16 篇文章 0 订阅
给出 n 个数的序列,先把每个数逆序(如 120 变为 21,123 变为 321),将会产生 n 个新数,我们把它称作序列 A。

之后对序列 A 中的 n-1 个数进行 q 次操作,每次操作将会把序列 A 上的某个数替换成另一个数,并询问序列 A 中任取连续的一段数,如何取才能使这段数的和最大,请你输出最大值。

Input

输入数据有多组(数据组数不超过 100)。

对于每组数据:

  • 第一行输入 n, q (1 <= n <= 10000, 1 <= q <= 100) 分别表示数字个数和操作次数
  • 接下来的一行输入 n 个正整数,最大不超过 10000
  • 接下来的 q 行,每行输入 2 个整数,分别为要替换的数的位置 xi (1 <= xi <= n) 和要修改的值 yi (-10000 <= yi <= 10000)

当 n = 0 时输入结束。

Output

对于每组数据第一行输出 "Case #x:" (不包括引号,冒号后无空格),x 表示当前为第几组数据。之后对应 q 次操作每次操作输出一行,表示查询的答案。

Example Input
5 2
1 2 3 4 5
2 -5
2 5
3 3
45 12 32
3 -5
1 5
2 10
0
Example Output
Case #1:
12
18
Case #2:
75
26
15


这道题是我们专业的期末考试上机题,当时时间比较紧张,只有一位大佬做了出来。我当时读完这道题时就知道是动归,所以就果断放弃了...

到最后回首才发现... 这道题似曾相识... 原来寒假的时候遇到过最大子序列和的问题,当时还知道两种方法,分治递归和动归,只不过这里就是变化了一下,把每个数字翻转一下,所以,就是多写个函数的问题...

动归解法:

#include <stdio.h>
#include <stdlib.h>
#define N 10010

int a[N];

int maxsum(int length)
{
	int i,sum=0,max=0;
	for(i=0;i<length;i++)
	{
		sum+=a[i];
		if(sum<0)
			sum=0;
		else if(sum>max)
			max=sum;
	}
	return max;
}
int f(int n)
{
	int s[6];
	int i,x=0;
	while(n!=0)
	{
		s[x++]=n%10;
		n/=10;
	}
	for(i=0;i<x;i++)
	{
		n=n*10+s[i];
	}
	return n;
}
int main()
{
	int n,q,x,y;
	int i,t=0;
	while(~scanf("%d",&n)&&n!=0)
	{
		scanf("%d",&q);
		for(i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			a[i]=f(a[i]);
		}
		printf("Case #%d:\n",++t);
		while(q--)
		{
			scanf("%d%d",&x,&y);
			a[x-1]=y;
			printf("%d\n",maxsum(n));
		}
	}
	return 0;
}

分治递归解法:

#include <stdio.h>
#include <stdlib.h>
#define N 10010

int a[N];

int maxsum(int left,int right)
{
	int sum,i;
	if(left==right)
		return a[left];
	int mid=(left+right)/2;
	int leftsum=maxsum(left,mid);
	int rightsum=maxsum(mid+1,right);
	int lefts=0,sum1=0;
	for(i=mid;i>=left;i--)
	{
		lefts+=a[i];
		if(lefts>sum1)
			sum1=lefts;
	}	
	int rights=0,sum2=0;
	for(i=mid+1;i<=right;i++)
	{
		rights+=a[i];
		if(rights>sum2)
			sum2=rights;
	}
	sum=leftsum>rightsum?leftsum:rightsum;
	sum=sum>(sum1+sum2)?sum:(sum1+sum2);
	return sum;
}
int f(int n)
{
	int s[6];
	int i,x=0;
	while(n!=0)
	{
		s[x++]=n%10;
		n/=10;
	}
	for(i=0;i<x;i++)
	{
		n=n*10+s[i];
	}
	return n;
}
int main()
{
	int n,q,x,y;
	int i,t=0;
	while(~scanf("%d",&n)&&n!=0)
	{
		scanf("%d",&q);
		for(i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			a[i]=f(a[i]);
		}
		printf("Case #%d:\n",++t);
		while(q--)
		{
			scanf("%d%d",&x,&y);
			a[x-1]=y;
			printf("%d\n",maxsum(0,n-1));
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值