【郑轻oj】1837-LT说我不服(最大子序列的和)(好题)

1837: LT说我不服

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 53   Solved: 12

Submit Status Web Board

Description

对于上一道题目LT不服,表示那么简单的题目不屑于去做,所以我们决定加大一下题目的难度,下面是我们LT出的题目:
假如给你一个由n个数组成的序列A1, A2, A3, A4 …… An。你可以选择任意一个大小的区间,将其中的每一个数x变成(x*1888+101)%14507。
求这n个数的最大和可能是多少。

Input

输入有多组数据
每组数据第一行输入一个整数n为序列元素个数。(1 <= n <= 100000)
第二行n个整数A1, A2, A3, A4 …… An。(0 <= Ai <= 10000)

Output

每组样例输出一行答案。

Sample Input

210000 999951 9999 1 9999 1

Sample Output

1999921989

HINT

 范围在int内

Source

LT系列


第一次接触最小子序列的和的算法,提交两次,超时一次,第二次A了。

超时用了分冶的算法, 第二次的算法不好解释的通,我这里就自己写写试试吧(估计也只有自己懂了):从前往后逐项算和,不断地把最大的和保存入变量中,当和小于0时,便停止此轮的运算,从下一个数开始继续算和,直到结束。为什么可以这么算呢?根据个人的理解,既然每逢负数就从下一项开始重新计算,那么后面的最优解肯定是不包括前面的,而前面的最优解已经保存入变量中,如此筛选出被每个负数分开的子序列的和,最优解也一次次的更新,并不会漏解(感觉跟没说一样)。


先看超时代码吧,虽然超时了,但是分冶的思路还是挺好的:

TL代码

#include <stdio.h>
int a[100022],b[100022];		//原序列,相减得的子序列

int count_leftmax(int left,int center)		//自定义函数:左边序列的最大和 
{
	int max=0;
	int sum;
	if (left==center && b[left]<0)
		return 0;
	else
	{
		for (int i=left;i<=center;i++)
		{
			sum=0;
			for (int j=i;j<=center;j++)
			{
				sum+=b[j];
				if (sum>max)
					max=sum;
			}
		}
		return max;
	}
}
 
int count_rightmax(int center,int right)		//自定义函数:右边序列的最大和 
{
	int max=0;
	int sum;
	center++;		//center归左边 
	if (right==center && b[right]<0)
		return 0;
	else
	{
		for (int i=center;i<=right;i++)
		{
			sum=0;
			for (int j=i;j<=right;j++)
			{
				sum+=b[j];
				if (sum>max)
					max=sum;
			}
		}
		return max;
	}
}

int count_doublemax(int left,int center,int right)
{
	int max;
	int max1=0;
	int max2=0;
	int sum;
	//先算左边
	if (left==center && b[left]<0)
		return 0;
	else
	{
		sum=0;
		for (int j=center;j>=left;j--)
		{
			sum+=b[j];
			if (sum>max1)
				max1=sum;
		}
	}
	//再算右边
	center++;
	if (right==center && b[right]<0)
		return 0;
	else
	{
		sum=0;
		for (int j=center;j<=right;j++)
		{
			sum+=b[j];
			if (sum>max2)
				max2=sum;
		}
	}
	max=max1+max2;
	return max;
}

int cmp(int leftmax,int rightmax,int centermax)		//取三个的最大值 
{
	if (leftmax>rightmax)
	{
		if (leftmax>centermax)
			return leftmax;
		else
			return centermax;
	}
	else
	{
		if (rightmax>centermax)
			return rightmax;
		else
			return centermax;
	}
}

int main()
{
	int n;
	int left,right,center;
	int leftmax,rightmax,centermax;
	int max;
	int sum;		//用来对原数列相加 
	while (~scanf ("%d",&n))
	{
		for (int i=1;i<=n;i++)
		{
			scanf ("%d",&a[i]);
			b[i]=(a[i]*1888+101)%14507;
			b[i]=b[i]-a[i];
		}
		left=1;
		right=n;
		center=(left+right)/2;		//中心的数归左边
		leftmax=count_leftmax(left,center);
		rightmax=count_rightmax(center,right);
		centermax=count_doublemax(left,center,right);
		max=cmp(leftmax,rightmax,centermax);
		sum=0;
		for (int i=1;i<=n;i++)
		{
			sum+=a[i];
		}
		sum+=max;
		printf ("%d\n",sum);
	}
	return 0;
}

代码略长 — —! 但是思路还是很明确的,代码可以进一步优化。


下面是AC代码:

#include <stdio.h>
int main()
{
	int u;
	int a[100022],b[100022];
	int max,tsum;
	while (~scanf ("%d",&u))
	{
		for (int i=1;i<=u;i++)
		{
			scanf ("%d",&a[i]);
			b[i]=(a[i]*1888+101)%14507;
			b[i]=b[i]-a[i];
		}
		max=0;
		tsum=0;
		for (int i=1;i<=u;i++)
		{
			tsum+=b[i];
			if (tsum>max)
				max=tsum;
			if (tsum<0)
			{
				tsum=0;
				continue;
			}
		}
		int cot=0;
		for (int i=1;i<=u;i++)
		{
			cot+=a[i];
		}
		cot+=max;
		printf ("%d\n",cot);
	}
	return 0;
}

嗯挺短的!算法很重要!





你可能感兴趣的:(【郑轻oj】1837-LT说我不服(最大子序列的和)(好题))