(The largest sub-segment and questions) solution to a problem Luo Gu P1115 (enumeration enumeration optimization + + + dp compare divide and conquer)

P1115 and the largest sub-segment

Title Description

Given a sequence, wherein the selected period of continuous and non-empty and that this maximum.

Input Format

The first line is a positive integer N , denotes the length of the sequence.

The second line contains N N absolute value is not greater than 10,000 integer A I , this sequence is described.

Output Format

An integer, the largest sub-segment and how much. The minimum sub-segment length is 1.

Sample input and output

Entry 
7
2 -4 3 -1 2 -4 3
Export
4

Description / Tips

[Sample Description]

2, -4,3, -1,2, -4,3 , the biggest sub-segment and 4, the sub-segment 3, 1,2 .

[Agreed] with the scale data

For . 4 0 % of the data, there are N 2 0 0 0.

For . 1 0 0 % of the data, there are N 2 0 0 0 0 0.

Various methods described:

Pure violence Solution:

This method is nothing to say, directly in the accumulation and enumerate each of the boundary, and judges whether the largest, it can be replaced, the time complexity of the algorithm is O (N . 3 ).

Code:

#include <stdio.h>
int a[200000];
int main  (void)
{
	int n, i, j, sum, max, start;
	max = -1e9;
	scanf("%d", &n);
	for(i = 0; i < n;  i++)
		scanf("%d", &a[i]);
	for(i = 0; i < n; i++)
		for(j = i; j < n; j++)
		{
			sum = 0;
			for(start = i; start <= j; start++) 
				sum += a[start];
			if(sum > max)
				max = sum;
		}
		printf("%d", max);
	return 0;
}

 

  result:

 

 

Violence Optimization:

The beginning and end positions due to the two traversing range, a tail loop has to start from scratch each time increment, so we can actually add value when incremented, it is determined whether or not the maximum, the time complexity of the algorithm is O (N 2 )

Code:

#include <stdio.h>
int a[200000];
int main  (void)
{
  int n, i, j, sum, max, start;
  max = -1e9;
  scanf("%d", &n);
  for(i = 0; i < n;  i++)
    scanf("%d", &a[i]);   for(i = 0; i < n; i++) {
    sum = 0;     for(j = i; j < n; j++)     {      sum += a[j];   if(sum > max)     max = sum;   }   }   printf("%d", max);   return 0; }

 

  

 

result:

Minute root treatment:

The so-called divide and conquer, is forced to put several split in half, then we easy to get, the largest sub-sequence must be in the first half of the largest sub-sequence or the second half of the largest sub-sequence or at the end of the first half of the piece sequence for the first half of the range the largest sub-sequence beginning with the second half of the piece at the end of the sequence is the beginning of the second half of the largest range of sub-sequences and can only be the three cases. We use recursion to implement partition, each time the program returns are the largest sub-sequence within the segment, and finally be able to achieve the greatest find in the whole of sub-sequences. We artificially split in half the array by recursively obtain the maximum left and right section, and then traverse the segment from the middle to both sides, to find the maximum subsequence within the range from the middle to the left and to the right of the intermediate +1, and the two together, and to which the left and right section compares the maximum sequence and returns the maximum to obtain the largest segment in the sequence. Calculation complexity O (NlogN).

Code:

#include <stdio.h> 
int A [200000], max; 
int MAXN (int X, Y int, int Z) 
{ 
	IF (X> = Y && X> = Z) 
		return X; 
	the else IF (Y> X = Y &&> = Z) 
		return Y; 
	the else 
		return Z; 
} 
int Divide (int left, int right) 
{ 
	IF (left == right) 
		return A [left]; 
	int L = 0, R & lt = 0, = -1e9 maxlt , maxrt = -1e9; // maxrt a maximum rightward starting from intermediate sequences, maxlt maximum leftward from intermediate 
	int = mID (left + right) / 2; 
	int MaxL = Divide (left, mID); // maxl to the left of the maximum segment 
	int maxr = divide (mid + 1 , right); // maxr maximum right end 
	for (int I = MID; I> = left; i--) 
	{ 
		L + A = [I ]; 
		IF (L> maxlt) 
			maxlt = L; 
	}
	for(int i = mid + 1; i <= right; i++)
	{
		r += a[i];
		if(r > maxrt)
			maxrt = r;
	}
	return maxn(maxl, maxr, maxlt + maxrt);
}
int main  (void)
{
	int n, i, j;
	scanf("%d", &n);
	for(i = 0; i < n;  i++)
		scanf("%d", &a[i]);
	max = divide(0, n - 1);
	printf("%d", max);
	return 0;
}

 

  

 

 

result:

Dynamic Programming:

本题将序列从开头开始遍历,相当于序列的开头为数组的开头,序列的末尾从开头一直到末尾,每次得到的结果为该范围内最大的子序列。一开始最大子序列和就是a[0](因为序列开头和结尾都是第一个)。当一个序列中找到最大子序列后,读取下一个数,max要么是之前序列的最大值,要么是之前序列中子序列的末尾是之前序列末尾的最大子序列加上刚刚读取的数要么就是刚刚读取的数。如果之前序列中子序列的末尾为之前序列末尾的最大子序列小于0,那么该子序列加上刚刚读取的数肯定没有刚刚读取的数大,如果大于等于0,则该子序列加上刚刚读取的数要比刚刚读的数大,所以两者最大与之前序列最大的子序列之和比较,得到现在的最大子序列和,当到达末尾时,也就是这个数组的最大子序列。声明一个sum累加数,每加一个数都要判断sum是否比max要大,如果大就替换,如果sum < 0, 那么说明从此时的开始累加比此时的数加上之前累加出来的负值要大,因此将sum置为0(表明从此时开始算子序列,sum的值为当前的数),sum的值即是从当前位置向左得到的子序列中和最大的,因而与max进行比较。算法时间复杂度O(N)。

代码:

#include <stdio.h>
int a[200000], sum, max;
int main  (void)
{
	int n, i;
	max = -1e9;
	scanf("%d", &n);
	for(i = 0; i < n;  i++)
	{
		scanf("%d", &a[i]);
		if(sum < 0)//说明之前的序列中,子序列末尾为之前序列末尾的最大子序列之和为负数,加a[i]还不如a[i]大,所以置为0
			sum = 0;
		sum += a[i];
		if(sum > max)
			max = sum;
	}
	printf("%d", max);
	return 0;
}

 

  

 

结果:

其实采用dp的方法并不需要开数组进行存储,这样既节省了空间也节省了时间,这里只需要把a[i]换成一个变量即可(就不贴代码了)

Guess you like

Origin www.cnblogs.com/jacobfun/p/11262551.html